Here's a new version of the serdev bus support with all the review
feedback so far incorporated. I've left it named serdev for now pending
any further votes one way or the other, but I did rename the sysfs visible
portions to "serial".
There's still some discussion about what to do with devices that pass thru
data to userspace unmodified like GPS and could still use tty device for
the data path. IMO, we should treat this as a separate problem following
this series. Drivers we want to convert to serdev and already in the
kernel don't need this functionality.
I need a SoB from Alan on his patch 2 and would like review from Alan and/or
Peter on the locking in patch 5.
I have hacked up versions of the BT ldisc and TI ST drivers moved over to
use the serdev bus. I have BT working on the HiKey board which has TI BT.
With the serdev bus support, it eliminates the need for the TI userspace
UIM daemon. I've made some progress cleaning up the TI-ST into proper
patches and also got it working at 3Mbps.
Changelog is in individual patches. Previous version is here[1]. This
series and the mentioned drivers can be found here[2].
Rob
[1] https://lkml.org/lkml/2017/1/6/411
[2] git://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git serial-bus-v3
Alan Cox (1):
tty_port: allow a port to be opened with a tty that has no file handle
Rob Herring (8):
tty: move the non-file related parts of tty_release to new
tty_release_struct
tty_port: make tty_port_register_device wrap
tty_port_register_device_attr
tty: constify tty_ldisc_receive_buf buffer pointer
tty_port: Add port client functions
dt/bindings: Add a serial/UART attached device binding
serdev: Introduce new bus for serial attached devices
serdev: add a tty port controller driver
tty_port: register tty ports with serdev bus
.../devicetree/bindings/serial/slave-device.txt | 36 ++
MAINTAINERS | 8 +
drivers/char/Kconfig | 1 +
drivers/tty/Makefile | 1 +
drivers/tty/serdev/Kconfig | 16 +
drivers/tty/serdev/Makefile | 5 +
drivers/tty/serdev/core.c | 421 +++++++++++++++++++++
drivers/tty/serdev/serdev-ttyport.c | 240 ++++++++++++
drivers/tty/tty_buffer.c | 19 +-
drivers/tty/tty_io.c | 52 ++-
drivers/tty/tty_port.c | 58 ++-
include/linux/serdev.h | 234 ++++++++++++
include/linux/tty.h | 12 +-
13 files changed, 1062 insertions(+), 41 deletions(-)
create mode 100644 Documentation/devicetree/bindings/serial/slave-device.txt
create mode 100644 drivers/tty/serdev/Kconfig
create mode 100644 drivers/tty/serdev/Makefile
create mode 100644 drivers/tty/serdev/core.c
create mode 100644 drivers/tty/serdev/serdev-ttyport.c
create mode 100644 include/linux/serdev.h
--
2.10.1
On Fri 2017-01-20 18:16:11, Andy Shevchenko wrote:
> On Fri, 2017-01-20 at 16:26 +0100, Linus Walleij wrote:
>
> > > Now, NMEA knows about some of the complexity (not AGPS), but gets
> > > the
> > > details wrong. In particular, it would be good to have error
> > > estimates
> > > and velocities from the same moment you get position estimates.
> >
> > NMEA if it is this:
> > https://en.wikipedia.org/wiki/NMEA_0183
> >
> > Seems to be a high-level format such as XML or CSV or any other
> > $FAVOURITE_ASCII_TRANSPORT format.
>
> Not exactly. It depends on device settings.
> Old GPS I used to play with have AT command to switch between ASCII and
> binary format. Some of the devices might use non-standard binary
> protocols (however, representable as NMEA), etc.
Apparently NMEA has some serious limitations. gpsd authors have some
good docs about that.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi!
> >> In my simple opinion GPSes shound live in drivers/iio/gps simply by
> >> usecase association: streaming out a series of accelerometer readings
> >> periodically through IIOs chardevs and other data about the physical
> >> world is not any different from the GPS usecase that give you a stream
> >> of coordinates on where on this planet you are.
> >
> > That is... not quite how GPSes work. What interface would you propose?
> > It would be good to support error estimates in position/velocities and
> > AGPS data upload.
>
> Sorry for my ignorance. I have not had the opportunity to work
> directly with a GPS hardware.
Few people have. It is CDMA corelators at the low level.
> > Now, NMEA knows about some of the complexity (not AGPS), but gets the
> > details wrong. In particular, it would be good to have error estimates
> > and velocities from the same moment you get position estimates.
>
> NMEA if it is this:
> https://en.wikipedia.org/wiki/NMEA_0183
Yes.
> Seems to be a high-level format such as XML or CSV or any other
> $FAVOURITE_ASCII_TRANSPORT format.
>
> What we want to push into the ring buffer is of course the raw data
> that is produced by the GPS hardware, whatever that may be.
> If what we have is this text format we should not reverse-translate
> it back any more than modem AT commands, it doesn't make sense
> I guess.
Umm. Its hairy. Each GPS talks slightly different version of NMEA
:-(. But yes, we have userland driver called 'gpsd' that understands
most of them. Many GPSes have additional, non-NMEA protocol you can
switch them to.
> So NMEA processing would be in userspace. And if the GPS is just
> streaming this text data over to the client, like Marcel says, it is
> more reasonable to just feed that up to userspace (no policy in the
> kernel).
Well -- we normally do hardware abstraction in the kernel :-).
> I am however aware of certain hardware such as the ST
> Microelectronis STA2062 produced for Garmin, which is *not*
> connected to any external chip, and is not talking over serial
> to any RSx port, and does not have an embedded firmware running
> on another SoC in any special GPS chip. And it is unassisted.
I googled STA2062 but it does not make much sense to me.
Yes, not everything talks NMEA. Nokia N900 is an example, and it has
in-tree driver.
> Maybe that is an oddity. In the mobile phone business I guess it
> could be more common to have a separate SoC that by way
> of standards just stream NMEA data.
Sorry, I don't understand. Yes, in mobile phones GPSes that produce
something else than NMEA are pretty common.
> Fusing that with other sensor data (accelerometer, compass, etc)
> is again indeed a userspace task. I hope NMEA includes very good
> timestamps.
Umm. Timestamps are quite hard to do over serial :-(.
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
On Fri, 2017-01-20 at 16:26 +0100, Linus Walleij wrote:
> > Now, NMEA knows about some of the complexity (not AGPS), but gets
> > the
> > details wrong. In particular, it would be good to have error
> > estimates
> > and velocities from the same moment you get position estimates.
>
> NMEA if it is this:
> https://en.wikipedia.org/wiki/NMEA_0183
>
> Seems to be a high-level format such as XML or CSV or any other
> $FAVOURITE_ASCII_TRANSPORT format.
Not exactly. It depends on device settings.
Old GPS I used to play with have AT command to switch between ASCII and
binary format. Some of the devices might use non-standard binary
protocols (however, representable as NMEA), etc.
--
Andy Shevchenko <[email protected]>
Intel Finland Oy
oOn Fri, Jan 20, 2017 at 3:22 PM, Pavel Machek <[email protected]> wrote:
> Switched subject: Rob's work is great for GPS and bluetooth, but this
> goes beyond it.
Sure why not talk around a bit.
>> In my simple opinion GPSes shound live in drivers/iio/gps simply by
>> usecase association: streaming out a series of accelerometer readings
>> periodically through IIOs chardevs and other data about the physical
>> world is not any different from the GPS usecase that give you a stream
>> of coordinates on where on this planet you are.
>
> That is... not quite how GPSes work. What interface would you propose?
> It would be good to support error estimates in position/velocities and
> AGPS data upload.
Sorry for my ignorance. I have not had the opportunity to work
directly with a GPS hardware.
> Now, NMEA knows about some of the complexity (not AGPS), but gets the
> details wrong. In particular, it would be good to have error estimates
> and velocities from the same moment you get position estimates.
NMEA if it is this:
https://en.wikipedia.org/wiki/NMEA_0183
Seems to be a high-level format such as XML or CSV or any other
$FAVOURITE_ASCII_TRANSPORT format.
What we want to push into the ring buffer is of course the raw data
that is produced by the GPS hardware, whatever that may be.
If what we have is this text format we should not reverse-translate
it back any more than modem AT commands, it doesn't make sense
I guess.
So NMEA processing would be in userspace. And if the GPS is just
streaming this text data over to the client, like Marcel says, it is
more reasonable to just feed that up to userspace (no policy in the
kernel).
I am however aware of certain hardware such as the ST
Microelectronis STA2062 produced for Garmin, which is *not*
connected to any external chip, and is not talking over serial
to any RSx port, and does not have an embedded firmware running
on another SoC in any special GPS chip. And it is unassisted.
Maybe that is an oddity. In the mobile phone business I guess it
could be more common to have a separate SoC that by way
of standards just stream NMEA data.
Fusing that with other sensor data (accelerometer, compass, etc)
is again indeed a userspace task. I hope NMEA includes very good
timestamps.
Thanks,
Linus Walleij
> Am 20.01.2017 um 15:14 schrieb Marcel Holtmann <[email protected]>:
>=20
> Hi Linus,
>=20
>>> There's still some discussion about what to do with devices that =
pass thru
>>> data to userspace unmodified like GPS and could still use tty device =
for
>>> the data path. IMO, we should treat this as a separate problem =
following
>>> this series. Drivers we want to convert to serdev and already in the
>>> kernel don't need this functionality.
>>=20
>> In my simple opinion GPSes shound live in drivers/iio/gps simply by
>> usecase association: streaming out a series of accelerometer readings
>> periodically through IIOs chardevs and other data about the physical
>> world is not any different from the GPS usecase that give you a =
stream
>> of coordinates on where on this planet you are.
>>=20
>> The fact that vendors like to defer GPS processing to userspace =
because
>> it is considered "secret sauce" is not the concern of the kernel =
community,
>> though problems like that in general is the great tragedy of our =
time.
>>=20
>> It would be fun to see a pure, reverse-engineered GPS driver in IIO.
>=20
> except for the pure NMEA devices. Which are pretty much defined as =
terminal devices using RS422 and 4800 baud. For anything non-NMEA, I =
would agree that using IIO might be a good option. So instead of a GPS =
subsystem, might just have a GPS class / type in the IIO subsystem.
Well, we could implement a default NMEA parser in the iio subsystem to
translate satellite time, geolocation, orientation, speed, and satellite
signal strengths to iio channels.
On the other hand we have to wait until all user-space GPS applications
use it and appear in wider use in distribution. Let's say in 3-4 years?
Therefore, I'd like to see NMEA records passed to user space and not
shielded completely.
And some GPS devices send extensions to the core set of NEMA records,
which might give other useful information. Having this needs some
extensibility to the iio translation or side-channels.
BR,
Nikolaus
Hi!
Switched subject: Rob's work is great for GPS and bluetooth, but this
goes beyond it.
> On Mon, Jan 16, 2017 at 11:54 PM, Rob Herring <[email protected]> wrote:
>
> > There's still some discussion about what to do with devices that pass thru
> > data to userspace unmodified like GPS and could still use tty device for
> > the data path. IMO, we should treat this as a separate problem following
> > this series. Drivers we want to convert to serdev and already in the
> > kernel don't need this functionality.
>
> In my simple opinion GPSes shound live in drivers/iio/gps simply by
> usecase association: streaming out a series of accelerometer readings
> periodically through IIOs chardevs and other data about the physical
> world is not any different from the GPS usecase that give you a stream
> of coordinates on where on this planet you are.
That is... not quite how GPSes work. What interface would you propose?
It would be good to support error estimates in position/velocities and
AGPS data upload.
Now, NMEA knows about some of the complexity (not AGPS), but gets the
details wrong. In particular, it would be good to have error estimates
and velocities from the same moment you get position estimates.
> The fact that vendors like to defer GPS processing to userspace because
> it is considered "secret sauce" is not the concern of the kernel community,
> though problems like that in general is the great tragedy of our time.
>
> It would be fun to see a pure, reverse-engineered GPS driver in IIO.
Well, many GPSes simply produce NMEA, and we have drivers for some other.
Here's example driver:
https://gitlab.com/tui/tui/blob/master/ofone/gps3.c
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Hi Linus,
>> There's still some discussion about what to do with devices that pass thru
>> data to userspace unmodified like GPS and could still use tty device for
>> the data path. IMO, we should treat this as a separate problem following
>> this series. Drivers we want to convert to serdev and already in the
>> kernel don't need this functionality.
>
> In my simple opinion GPSes shound live in drivers/iio/gps simply by
> usecase association: streaming out a series of accelerometer readings
> periodically through IIOs chardevs and other data about the physical
> world is not any different from the GPS usecase that give you a stream
> of coordinates on where on this planet you are.
>
> The fact that vendors like to defer GPS processing to userspace because
> it is considered "secret sauce" is not the concern of the kernel community,
> though problems like that in general is the great tragedy of our time.
>
> It would be fun to see a pure, reverse-engineered GPS driver in IIO.
except for the pure NMEA devices. Which are pretty much defined as terminal devices using RS422 and 4800 baud. For anything non-NMEA, I would agree that using IIO might be a good option. So instead of a GPS subsystem, might just have a GPS class / type in the IIO subsystem.
Regards
Marcel
On Mon, Jan 16, 2017 at 11:54 PM, Rob Herring <[email protected]> wrote:
> There's still some discussion about what to do with devices that pass thr=
u
> data to userspace unmodified like GPS and could still use tty device for
> the data path. IMO, we should treat this as a separate problem following
> this series. Drivers we want to convert to serdev and already in the
> kernel don't need this functionality.
In my simple opinion GPSes shound live in drivers/iio/gps simply by
usecase association: streaming out a series of accelerometer readings
periodically through IIOs chardevs and other data about the physical
world is not any different from the GPS usecase that give you a stream
of coordinates on where on this planet you are.
The fact that vendors like to defer GPS processing to userspace because
it is considered "secret sauce" is not the concern of the kernel community,
though problems like that in general is the great tragedy of our time.
It would be fun to see a pure, reverse-engineered GPS driver in IIO.
Just my =E2=82=AC0.01
(And by the way: awesome work on this series.)
Yours,
Linus Walleij
On Mon, Jan 16, 2017 at 04:54:27PM -0600, Rob Herring wrote:
> Here's a new version of the serdev bus support with all the review
> feedback so far incorporated. I've left it named serdev for now pending
> any further votes one way or the other, but I did rename the sysfs visible
> portions to "serial".
>
> There's still some discussion about what to do with devices that pass thru
> data to userspace unmodified like GPS and could still use tty device for
> the data path. IMO, we should treat this as a separate problem following
> this series. Drivers we want to convert to serdev and already in the
> kernel don't need this functionality.
>
> I need a SoB from Alan on his patch 2 and would like review from Alan and/or
> Peter on the locking in patch 5.
I've applied the first 4 patches now, can you respin this and hopefully
Peter will review that patch, as I'd like him to before taking it.
thanks,
greg k-h
Hi,
On Fri, Jan 20, 2017 at 02:36:12AM +0100, msuchanek wrote:
> On 2017-01-16 23:54, Rob Herring wrote:
> > Here's a new version of the serdev bus support with all the review
> > feedback so far incorporated. I've left it named serdev for now pending
> > any further votes one way or the other, but I did rename the sysfs
> > visible
> > portions to "serial".
> >
> > There's still some discussion about what to do with devices that pass
> > thru
> > data to userspace unmodified like GPS and could still use tty device for
> > the data path. IMO, we should treat this as a separate problem following
> > this series. Drivers we want to convert to serdev and already in the
> > kernel don't need this functionality.
>
> The whole point of the serial bus is to simplify and clean up support for
> serial devices.
> If tty users cannot use the kernel support for automagic kill
> switches/resets/whatever with kernel GPIO or whatever framework and must
> continue supporting userspace GPIO and hacks for writing IO space from
> userland for their devices there is just no point.
> I mean it's fine to add support for your single pet device but if you are
> to support non-trivial number of devices they don't get all perfect kernel
> driver overnight. And if you need userspace GPIO for half of your devices
> you can just continue using it for all to *simplify* your userspace code.
This is definitely not about a single pet device. It helps for most
of the serial attached bluetooth chips. For example my work on the
Nokia N-series bluetooth driver is waiting for this series and
supporting the nokia bluetooth chips with userspace GPIOs is more
or less impossible with the data being handled in the kernel, since
GPIOs have to be toggled based on that data.
Also there is the kurobox, which uses a serial attached board reset
controller. It may not need extra GPIOs/regulators/whatever, but it
must have a full-in-kernel driver, so that 'reboot' works as expected ;)
> It has already happened for SPI devices and the implementation of the
> userspace access to SPI is dragging for years.
Talking about dragging for years: This series has been initially
discussed in 2014. Implementing this step-by-step looks sensible
to me.
-- Sebastian
On 2017-01-16 23:54, Rob Herring wrote:
> Here's a new version of the serdev bus support with all the review
> feedback so far incorporated. I've left it named serdev for now pending
> any further votes one way or the other, but I did rename the sysfs
> visible
> portions to "serial".
>
> There's still some discussion about what to do with devices that pass
> thru
> data to userspace unmodified like GPS and could still use tty device
> for
> the data path. IMO, we should treat this as a separate problem
> following
> this series. Drivers we want to convert to serdev and already in the
> kernel don't need this functionality.
The whole point of the serial bus is to simplify and clean up support
for
serial devices.
If tty users cannot use the kernel support for automagic kill
switches/resets/whatever with kernel GPIO or whatever framework and must
continue supporting userspace GPIO and hacks for writing IO space from
userland for their devices there is just no point.
I mean it's fine to add support for your single pet device but if you
are
to support non-trivial number of devices they don't get all perfect
kernel
driver overnight. And if you need userspace GPIO for half of your
devices
you can just continue using it for all to *simplify* your userspace
code.
It has already happened for SPI devices and the implementation of the
userspace access to SPI is dragging for years.
Thanks
Michal
On Mon, Jan 16, 2017 at 04:54:31PM -0600, Rob Herring wrote:
> This is needed to work with the client operations which uses const ptrs.
>
> Really, the flags pointer could be const, too, but this would be a tree
> wide fix.
>
> Signed-off-by: Rob Herring <[email protected]>
> Reviewed-by: Andy Shevchenko <[email protected]>
> Reviewed-By: Sebastian Reichel <[email protected]>
I've applied the first 4 patches in this series to my tree now, thanks.
greg k-h
On Thu, Jan 19, 2017 at 9:05 AM, Rob Herring <[email protected]> wrote:
> On Thu, Jan 19, 2017 at 7:37 AM, Greg Kroah-Hartman
> <[email protected]> wrote:
>> On Mon, Jan 16, 2017 at 04:54:29PM -0600, Rob Herring wrote:
>>> From: Alan Cox <[email protected]>
>>>
>>> Let us create tty objects entirely in kernel space. Untested proposal to
>>> show why all the ideas around rewriting half the uart stack are not needed.
>>>
>>> With this a kernel created non file backed tty object could be used to handle
>>> data, and set terminal modes. Not all ldiscs can cope with this as N_TTY in
>>> particular has to work back to the fs/tty layer.
>>>
>>> The tty_port code is however otherwise clean of file handles as far as I can
>>> tell as is the low level tty port write path used by the ldisc, the
>>> configuration low level interfaces and most of the ldiscs.
>>>
>>> Currently you don't have any exposure to see tty hangups because those are
>>> built around the file layer. However a) it's a fixed port so you probably
>>> don't care about that b) if you do we can add a callback and c) you almost
>>> certainly don't want the userspace tear down/rebuild behaviour anyway.
>>>
>>> This should however be sufficient if we wanted for example to enumerate all
>>> the bluetooth bound fixed ports via ACPI and make them directly available.
>>> It doesn't deal with the case of a user opening a port that's also kernel
>>> opened and that would need some locking out (so it returned EBUSY if bound
>>> to a kernel device of some kind). That needs resolving along with how you
>>> "up" or "down" your new bluetooth device, or enumerate it while providing
>>> the existing tty API to avoid regressions (and to debug).
>>>
>>> Alan
>>> Reviewed-by: Andy Shevchenko <[email protected]>
>>> Reviewed-By: Sebastian Reichel <[email protected]>
>>> ---
>>> Alan, Need your SoB here.
>>
>> Rob, as this patch is flowing through you, I need your signed-off-by as
>> well if I am to take it.
>
> Right. I've added both for the next version.
Oh, I see you applied the 1st patch. Thanks. If you want to apply this
one now here's my S-o-B:
Signed-off-by: Rob Herring <[email protected]>
Patches 3 and 4 can be applied too.
For patch 5, I'd really like someone with more tty knowledge to comment on.
Rob
On Thu, Jan 19, 2017 at 7:37 AM, Greg Kroah-Hartman
<[email protected]> wrote:
> On Mon, Jan 16, 2017 at 04:54:29PM -0600, Rob Herring wrote:
>> From: Alan Cox <[email protected]>
>>
>> Let us create tty objects entirely in kernel space. Untested proposal to
>> show why all the ideas around rewriting half the uart stack are not needed.
>>
>> With this a kernel created non file backed tty object could be used to handle
>> data, and set terminal modes. Not all ldiscs can cope with this as N_TTY in
>> particular has to work back to the fs/tty layer.
>>
>> The tty_port code is however otherwise clean of file handles as far as I can
>> tell as is the low level tty port write path used by the ldisc, the
>> configuration low level interfaces and most of the ldiscs.
>>
>> Currently you don't have any exposure to see tty hangups because those are
>> built around the file layer. However a) it's a fixed port so you probably
>> don't care about that b) if you do we can add a callback and c) you almost
>> certainly don't want the userspace tear down/rebuild behaviour anyway.
>>
>> This should however be sufficient if we wanted for example to enumerate all
>> the bluetooth bound fixed ports via ACPI and make them directly available.
>> It doesn't deal with the case of a user opening a port that's also kernel
>> opened and that would need some locking out (so it returned EBUSY if bound
>> to a kernel device of some kind). That needs resolving along with how you
>> "up" or "down" your new bluetooth device, or enumerate it while providing
>> the existing tty API to avoid regressions (and to debug).
>>
>> Alan
>> Reviewed-by: Andy Shevchenko <[email protected]>
>> Reviewed-By: Sebastian Reichel <[email protected]>
>> ---
>> Alan, Need your SoB here.
>
> Rob, as this patch is flowing through you, I need your signed-off-by as
> well if I am to take it.
Right. I've added both for the next version.
Rob
On Mon, Jan 16, 2017 at 04:54:29PM -0600, Rob Herring wrote:
> From: Alan Cox <[email protected]>
>
> Let us create tty objects entirely in kernel space. Untested proposal to
> show why all the ideas around rewriting half the uart stack are not needed.
>
> With this a kernel created non file backed tty object could be used to handle
> data, and set terminal modes. Not all ldiscs can cope with this as N_TTY in
> particular has to work back to the fs/tty layer.
>
> The tty_port code is however otherwise clean of file handles as far as I can
> tell as is the low level tty port write path used by the ldisc, the
> configuration low level interfaces and most of the ldiscs.
>
> Currently you don't have any exposure to see tty hangups because those are
> built around the file layer. However a) it's a fixed port so you probably
> don't care about that b) if you do we can add a callback and c) you almost
> certainly don't want the userspace tear down/rebuild behaviour anyway.
>
> This should however be sufficient if we wanted for example to enumerate all
> the bluetooth bound fixed ports via ACPI and make them directly available.
> It doesn't deal with the case of a user opening a port that's also kernel
> opened and that would need some locking out (so it returned EBUSY if bound
> to a kernel device of some kind). That needs resolving along with how you
> "up" or "down" your new bluetooth device, or enumerate it while providing
> the existing tty API to avoid regressions (and to debug).
>
> Alan
> Reviewed-by: Andy Shevchenko <[email protected]>
> Reviewed-By: Sebastian Reichel <[email protected]>
> ---
> Alan, Need your SoB here.
Rob, as this patch is flowing through you, I need your signed-off-by as
well if I am to take it.
thanks,
greg k-h
On Wed, Jan 18, 2017 at 5:53 AM, Fr=C3=A9d=C3=A9ric Danis
<[email protected]> wrote:
> Hello,
>
> Le 16/01/2017 =C3=A0 23:54, Rob Herring a =C3=A9crit :
>>
>> ---
>> v2:
>> - Add modalias sysfs attr and uevent hook
>
> ...
>>
>> +static int serdev_uevent(struct device *dev, struct kobj_uevent_env *en=
v)
>> +{
>> + /* TODO: ACPI and platform modalias */
>> + return of_device_uevent_modalias(dev, env);
>> +}
>
> ...
>>
>> +static ssize_t modalias_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + ssize_t len =3D of_device_get_modalias(dev, buf, PAGE_SIZE - 2);
>> + buf[len] =3D '\n';
>> + buf[len+1] =3D 0;
>> + return len+1;
>> +}
>
>
> This prevents from building serdev as a module with following errors:
> ERROR: "of_device_uevent_modalias" [drivers/tty/serdev/serdev.ko]
> undefined!
> ERROR: "of_device_get_modalias" [drivers/tty/serdev/serdev.ko] undefine=
d!
Turns out, there's a fix already[1] that should be going in via Greg's tree=
.
Rob
[1] https://www.mail-archive.com/[email protected]/msg1299121.ht=
ml
On Wed, Jan 18, 2017 at 6:42 AM, Andy Shevchenko
<[email protected]> wrote:
> On Mon, 2017-01-16 at 16:54 -0600, Rob Herring wrote:
>> Add a serdev controller driver for tty ports.
>>
>> The controller is registered with serdev when tty ports are registered
>> with the TTY core. As the TTY core is built-in only, this has the side
>> effect of making serdev built-in as well.
>>
>
>>
>> +if SERIAL_DEV_BUS
>> +
>> +config SERIAL_DEV_CTRL_TTYPORT
>> + bool "Serial device TTY port controller"
>> + depends on TTY
>
>
>> + depends on SERIAL_DEV_BUS != m
>
> Since you have this line the
> if SERIAL_DEV_BUS
> is redundant for it.
It is not. It is the standard pattern of
menuconfig BLAH
if BLAH
...
endif
<EOF>
If I remove the "if", then SERIAL_DEV_CTRL_TTYPORT can be enabled when
SERIAL_DEV_BUS=n which breaks the build
> So, leave either one or another (as an example you can look at
> DMADEVICES).
>
>> +
>> +#define SERPORT_BUSY 1
>> +#define SERPORT_ACTIVE 2
>> +#define SERPORT_DEAD 3
>> +
>> +struct serport {
>> + struct tty_port *port;
>> + struct tty_struct *tty;
>
>> + struct tty_driver *tty_drv;
>> + int tty_idx;
>
> Do you need tty_ prefix for them?
It's just to be clear it's the tty driver and index rather than this
driver's driver or index.
>> +static int ttyport_open(struct serdev_controller *ctrl)
>> +{
>> + struct serport *serport =
>> serdev_controller_get_drvdata(ctrl);
>> + struct tty_struct *tty;
>> + struct ktermios ktermios;
>> +
>> + tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
>> + serport->tty = tty;
>> +
>> + serport->port->client_ops = &client_ops;
>> + serport->port->client_data = ctrl;
>> +
>>
>
>> + tty->receive_room = 65536;
>
> Magic?
Probably. It's just what every ldisc uses. I suppose we could need
clients to set this, but we can add that as needed.
>> + if (tty->ops->open)
>> + tty->ops->open(serport->tty, NULL);
>> + else
>> + tty_port_open(serport->port, tty, NULL);
>> +
>> + /* Bring the UART into a known 8 bits no parity hw fc state
>> */
>> + ktermios = tty->termios;
>> + ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
>> + INLCR | IGNCR | ICRNL | IXON);
>> + ktermios.c_oflag &= ~OPOST;
>> + ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG |
>> IEXTEN);
>> + ktermios.c_cflag &= ~(CSIZE | PARENB);
>> + ktermios.c_cflag |= CS8;
>> + ktermios.c_cflag |= CRTSCTS;
>> + tty_set_termios(tty, &ktermios);
>> +
>> + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
>> +
>>
>
>> + mutex_lock(&serport->lock);
>> + set_bit(SERPORT_ACTIVE, &serport->flags);
>> + mutex_unlock(&serport->lock);
>
> So, some clarification would be good to have to understand why you need
> mutex _and_ atomic operation together.
>
> What does mutex protect?
Paranoia. Actually, looking at this closer, we can get rid of the
mutex altogether.
>> +void serdev_tty_port_unregister(struct tty_port *port)
>> +{
>> + struct serdev_controller *ctrl = port->client_data;
>> + struct serport *serport =
>> serdev_controller_get_drvdata(ctrl);
>> +
>
>> + if (!serport)
>> + return;
>
> What this check prevents from?
Didn't you ask this last time? See patch #9. tty_port_destructor()
calls this unconditionally as it doesn't know whether there's a serdev
or not. ctrl may be NULL, and then serport may be NULL.
Rob
On Mon, 2017-01-16 at 16:54 -0600, Rob Herring wrote:
> Add a serdev controller driver for tty ports.
>
> The controller is registered with serdev when tty ports are registered
> with the TTY core. As the TTY core is built-in only, this has the side
> effect of making serdev built-in as well.
>
>
> +if SERIAL_DEV_BUS
> +
> +config SERIAL_DEV_CTRL_TTYPORT
> + bool "Serial device TTY port controller"
> + depends on TTY
> + depends on SERIAL_DEV_BUS != m
Since you have this line the
if SERIAL_DEV_BUS
is redundant for it.
So, leave either one or another (as an example you can look at
DMADEVICES).
> +
> +#define SERPORT_BUSY 1
> +#define SERPORT_ACTIVE 2
> +#define SERPORT_DEAD 3
> +
> +struct serport {
> + struct tty_port *port;
> + struct tty_struct *tty;
> + struct tty_driver *tty_drv;
> + int tty_idx;
Do you need tty_ prefix for them?
> + struct mutex lock;
> + unsigned long flags;
> +};
> +
> +/*
> + * Callback functions from the tty port.
> + */
> +
> +static int ttyport_receive_buf(struct tty_port *port, const unsigned
> char *cp,
> + const unsigned char *fp, size_t
> count)
> +{
> + struct serdev_controller *ctrl = port->client_data;
> + struct serport *serport =
> serdev_controller_get_drvdata(ctrl);
> +
> + mutex_lock(&serport->lock);
> +
> + if (!test_bit(SERPORT_ACTIVE, &serport->flags))
So, if you are going to use serport->flags always under lock, you don't
need to use atomic bit operations.
Either
__test_bit() and Co
Or
flags & BIT(x)
> + goto out_unlock;
> +
> + serdev_controller_receive_buf(ctrl, cp, count);
> +
> +out_unlock:
> + mutex_unlock(&serport->lock);
> + return count;
> +}
> +
> +static void ttyport_write_wakeup(struct tty_port *port)
> +{
> + struct serdev_controller *ctrl = port->client_data;
> + struct serport *serport =
> serdev_controller_get_drvdata(ctrl);
> +
> + clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags);
> +
> + if (test_bit(SERPORT_ACTIVE, &serport->flags))
Hmm...
> + serdev_controller_write_wakeup(ctrl);
> +}
>
> + return tty_write_room(tty);
> +}
> +
> +
One extra line.
> +static int ttyport_open(struct serdev_controller *ctrl)
> +{
> + struct serport *serport =
> serdev_controller_get_drvdata(ctrl);
> + struct tty_struct *tty;
> + struct ktermios ktermios;
> +
> + tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
> + serport->tty = tty;
> +
> + serport->port->client_ops = &client_ops;
> + serport->port->client_data = ctrl;
> +
>
> + tty->receive_room = 65536;
Magic?
> +
> + if (tty->ops->open)
> + tty->ops->open(serport->tty, NULL);
> + else
> + tty_port_open(serport->port, tty, NULL);
> +
> + /* Bring the UART into a known 8 bits no parity hw fc state
> */
> + ktermios = tty->termios;
> + ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
> + INLCR | IGNCR | ICRNL | IXON);
> + ktermios.c_oflag &= ~OPOST;
> + ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG |
> IEXTEN);
> + ktermios.c_cflag &= ~(CSIZE | PARENB);
> + ktermios.c_cflag |= CS8;
> + ktermios.c_cflag |= CRTSCTS;
> + tty_set_termios(tty, &ktermios);
> +
> + set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
> +
>
> + mutex_lock(&serport->lock);
> + set_bit(SERPORT_ACTIVE, &serport->flags);
> + mutex_unlock(&serport->lock);
So, some clarification would be good to have to understand why you need
mutex _and_ atomic operation together.
What does mutex protect?
> +
> + tty_unlock(serport->tty);
> + return 0;
> +}
> +void serdev_tty_port_unregister(struct tty_port *port)
> +{
> + struct serdev_controller *ctrl = port->client_data;
> + struct serport *serport =
> serdev_controller_get_drvdata(ctrl);
> +
> + if (!serport)
> + return;
What this check prevents from?
> +
> + serdev_controller_remove(ctrl);
> + port->client_ops = NULL;
> + port->client_data = NULL;
> + serdev_controller_put(ctrl);
> +}
--
Andy Shevchenko <[email protected]>
Intel Finland Oy
Hello,
Le 16/01/2017 ? 23:54, Rob Herring a ?crit :
> ---
> v2:
> - Add modalias sysfs attr and uevent hook
...
> +static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env)
> +{
> + /* TODO: ACPI and platform modalias */
> + return of_device_uevent_modalias(dev, env);
> +}
...
> +static ssize_t modalias_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2);
> + buf[len] = '\n';
> + buf[len+1] = 0;
> + return len+1;
> +}
This prevents from building serdev as a module with following errors:
ERROR: "of_device_uevent_modalias" [drivers/tty/serdev/serdev.ko]
undefined!
ERROR: "of_device_get_modalias" [drivers/tty/serdev/serdev.ko] undefined!
Currently, those symbols are not exported in drivers/of/device.c
Fr?d?ric Danis
On Mon, 16 Jan 2017 16:54:29 -0600
Rob Herring <[email protected]> wrote:
> From: Alan Cox <[email protected]>
>
> Let us create tty objects entirely in kernel space. Untested proposal to
> show why all the ideas around rewriting half the uart stack are not needed.
>
> With this a kernel created non file backed tty object could be used to handle
> data, and set terminal modes. Not all ldiscs can cope with this as N_TTY in
> particular has to work back to the fs/tty layer.
>
> The tty_port code is however otherwise clean of file handles as far as I can
> tell as is the low level tty port write path used by the ldisc, the
> configuration low level interfaces and most of the ldiscs.
>
> Currently you don't have any exposure to see tty hangups because those are
> built around the file layer. However a) it's a fixed port so you probably
> don't care about that b) if you do we can add a callback and c) you almost
> certainly don't want the userspace tear down/rebuild behaviour anyway.
>
> This should however be sufficient if we wanted for example to enumerate all
> the bluetooth bound fixed ports via ACPI and make them directly available.
> It doesn't deal with the case of a user opening a port that's also kernel
> opened and that would need some locking out (so it returned EBUSY if bound
> to a kernel device of some kind). That needs resolving along with how you
> "up" or "down" your new bluetooth device, or enumerate it while providing
> the existing tty API to avoid regressions (and to debug).
>
> Alan
> Reviewed-by: Andy Shevchenko <[email protected]>
> Reviewed-By: Sebastian Reichel <[email protected]>
> ---
> Alan, Need your SoB here.
Signed-off-by: Alan Cox <[email protected]>
Alan
Hi!
> Here's a new version of the serdev bus support with all the review
> feedback so far incorporated. I've left it named serdev for now pending
> any further votes one way or the other, but I did rename the sysfs visible
> portions to "serial".
>
> There's still some discussion about what to do with devices that pass thru
> data to userspace unmodified like GPS and could still use tty device for
> the data path. IMO, we should treat this as a separate problem following
> this series. Drivers we want to convert to serdev and already in the
> kernel don't need this functionality.
>
> I need a SoB from Alan on his patch 2 and would like review from Alan and/or
> Peter on the locking in patch 5.
>
> I have hacked up versions of the BT ldisc and TI ST drivers moved over to
> use the serdev bus. I have BT working on the HiKey board which has TI BT.
> With the serdev bus support, it eliminates the need for the TI userspace
> UIM daemon. I've made some progress cleaning up the TI-ST into proper
> patches and also got it working at 3Mbps.
>
> Changelog is in individual patches. Previous version is here[1]. This
> series and the mentioned drivers can be found here[2].
Series looks good to me. Thanks for doing the work.
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Register a serdev controller with the serdev bus when a tty_port is
registered. This creates the serdev controller and create's serdev
devices for any DT child nodes of the tty_port's parent (i.e. the UART
device).
Signed-off-by: Rob Herring <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- Skip creating the tty device file node when serdev_tty_port_register
finds slave devices.
drivers/tty/tty_port.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 232a8cbf47bc..d7e0dced967b 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -16,6 +16,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/serdev.h>
static int tty_port_default_receive_buf(struct tty_port *port,
const unsigned char *p,
@@ -125,7 +126,15 @@ struct device *tty_port_register_device_attr(struct tty_port *port,
struct device *device, void *drvdata,
const struct attribute_group **attr_grp)
{
+ struct device *dev;
+
tty_port_link_device(port, driver, index);
+
+ dev = serdev_tty_port_register(port, device, driver, index);
+ if (PTR_ERR(dev) != -ENODEV)
+ /* Skip creating cdev if we registered a serdev device */
+ return dev;
+
return tty_register_device_attr(driver, index, device, drvdata,
attr_grp);
}
@@ -177,6 +186,9 @@ static void tty_port_destructor(struct kref *kref)
/* check if last port ref was dropped before tty release */
if (WARN_ON(port->itty))
return;
+
+ serdev_tty_port_unregister(port);
+
if (port->xmit_buf)
free_page((unsigned long)port->xmit_buf);
tty_port_destroy(port);
--
2.10.1
Add a serdev controller driver for tty ports.
The controller is registered with serdev when tty ports are registered
with the TTY core. As the TTY core is built-in only, this has the side
effect of making serdev built-in as well.
Signed-off-by: Rob Herring <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- Sort includes
- Made goto labels more descriptive
- Use tty-> instead of serdev->tty-> where possible
- Drop DT specific check in serdev_tty_port_register. DT specifics are only
in core bus code now.
- Comments on #endif's
- Use "depends on SERIAL_DEV_BUS != m" instead of "= y"
- Dropped module properties (not a module)
drivers/tty/serdev/Kconfig | 8 ++
drivers/tty/serdev/Makefile | 2 +
drivers/tty/serdev/serdev-ttyport.c | 240 ++++++++++++++++++++++++++++++++++++
include/linux/serdev.h | 21 ++++
4 files changed, 271 insertions(+)
create mode 100644 drivers/tty/serdev/serdev-ttyport.c
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
index 3b6ecd187bef..cdc6b820cf93 100644
--- a/drivers/tty/serdev/Kconfig
+++ b/drivers/tty/serdev/Kconfig
@@ -6,3 +6,11 @@ menuconfig SERIAL_DEV_BUS
help
Core support for devices connected via a serial port.
+if SERIAL_DEV_BUS
+
+config SERIAL_DEV_CTRL_TTYPORT
+ bool "Serial device TTY port controller"
+ depends on TTY
+ depends on SERIAL_DEV_BUS != m
+
+endif
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile
index 01a9b62183f4..0cbdb9444d9d 100644
--- a/drivers/tty/serdev/Makefile
+++ b/drivers/tty/serdev/Makefile
@@ -1,3 +1,5 @@
serdev-objs := core.o
obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
+
+obj-$(CONFIG_SERIAL_DEV_CTRL_TTYPORT) += serdev-ttyport.o
diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c
new file mode 100644
index 000000000000..bdd2db7db273
--- /dev/null
+++ b/drivers/tty/serdev/serdev-ttyport.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd., Rob Herring <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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 for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/serdev.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+
+#define SERPORT_BUSY 1
+#define SERPORT_ACTIVE 2
+#define SERPORT_DEAD 3
+
+struct serport {
+ struct tty_port *port;
+ struct tty_struct *tty;
+ struct tty_driver *tty_drv;
+ int tty_idx;
+ struct mutex lock;
+ unsigned long flags;
+};
+
+/*
+ * Callback functions from the tty port.
+ */
+
+static int ttyport_receive_buf(struct tty_port *port, const unsigned char *cp,
+ const unsigned char *fp, size_t count)
+{
+ struct serdev_controller *ctrl = port->client_data;
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+
+ mutex_lock(&serport->lock);
+
+ if (!test_bit(SERPORT_ACTIVE, &serport->flags))
+ goto out_unlock;
+
+ serdev_controller_receive_buf(ctrl, cp, count);
+
+out_unlock:
+ mutex_unlock(&serport->lock);
+ return count;
+}
+
+static void ttyport_write_wakeup(struct tty_port *port)
+{
+ struct serdev_controller *ctrl = port->client_data;
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+
+ clear_bit(TTY_DO_WRITE_WAKEUP, &port->tty->flags);
+
+ if (test_bit(SERPORT_ACTIVE, &serport->flags))
+ serdev_controller_write_wakeup(ctrl);
+}
+
+static const struct tty_port_client_operations client_ops = {
+ .receive_buf = ttyport_receive_buf,
+ .write_wakeup = ttyport_write_wakeup,
+};
+
+/*
+ * Callback functions from the serdev core.
+ */
+
+static int ttyport_write_buf(struct serdev_controller *ctrl, const unsigned char *data, size_t len)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ return tty->ops->write(serport->tty, data, len);
+}
+
+static void ttyport_write_flush(struct serdev_controller *ctrl)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+
+ tty_driver_flush_buffer(tty);
+}
+
+static int ttyport_write_room(struct serdev_controller *ctrl)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+
+ return tty_write_room(tty);
+}
+
+
+static int ttyport_open(struct serdev_controller *ctrl)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty;
+ struct ktermios ktermios;
+
+ tty = tty_init_dev(serport->tty_drv, serport->tty_idx);
+ serport->tty = tty;
+
+ serport->port->client_ops = &client_ops;
+ serport->port->client_data = ctrl;
+
+ tty->receive_room = 65536;
+
+ if (tty->ops->open)
+ tty->ops->open(serport->tty, NULL);
+ else
+ tty_port_open(serport->port, tty, NULL);
+
+ /* Bring the UART into a known 8 bits no parity hw fc state */
+ ktermios = tty->termios;
+ ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
+ INLCR | IGNCR | ICRNL | IXON);
+ ktermios.c_oflag &= ~OPOST;
+ ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+ ktermios.c_cflag &= ~(CSIZE | PARENB);
+ ktermios.c_cflag |= CS8;
+ ktermios.c_cflag |= CRTSCTS;
+ tty_set_termios(tty, &ktermios);
+
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+
+ mutex_lock(&serport->lock);
+ set_bit(SERPORT_ACTIVE, &serport->flags);
+ mutex_unlock(&serport->lock);
+
+ tty_unlock(serport->tty);
+ return 0;
+}
+
+static void ttyport_close(struct serdev_controller *ctrl)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+
+ mutex_lock(&serport->lock);
+
+ if (tty->ops->close)
+ tty->ops->close(tty, NULL);
+
+ tty_release_struct(tty, serport->tty_idx);
+
+ clear_bit(SERPORT_ACTIVE, &serport->flags);
+ mutex_unlock(&serport->lock);
+}
+
+static unsigned int ttyport_set_baudrate(struct serdev_controller *ctrl, unsigned int speed)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+ struct ktermios ktermios = tty->termios;
+
+ ktermios.c_cflag &= ~CBAUD;
+ tty_termios_encode_baud_rate(&ktermios, speed, speed);
+
+ /* tty_set_termios() return not checked as it is always 0 */
+ tty_set_termios(tty, &ktermios);
+ return speed;
+}
+
+static void ttyport_set_flow_control(struct serdev_controller *ctrl, bool enable)
+{
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+ struct tty_struct *tty = serport->tty;
+ struct ktermios ktermios = tty->termios;
+
+ if (enable)
+ ktermios.c_cflag |= CRTSCTS;
+ else
+ ktermios.c_cflag &= ~CRTSCTS;
+
+ tty_set_termios(tty, &ktermios);
+}
+
+struct serdev_controller_ops ctrl_ops = {
+ .write_buf = ttyport_write_buf,
+ .write_flush = ttyport_write_flush,
+ .write_room = ttyport_write_room,
+ .open = ttyport_open,
+ .close = ttyport_close,
+ .set_flow_control = ttyport_set_flow_control,
+ .set_baudrate = ttyport_set_baudrate,
+};
+
+struct device *serdev_tty_port_register(struct tty_port *port,
+ struct device *parent,
+ struct tty_driver *drv, int idx)
+{
+ struct serdev_controller *ctrl;
+ struct serport *serport;
+ int ret;
+
+ if (!port || !drv || !parent)
+ return ERR_PTR(-ENODEV);
+
+ ctrl = serdev_controller_alloc(parent, sizeof(struct serport));
+ if (!ctrl)
+ return ERR_PTR(-ENOMEM);
+ serport = serdev_controller_get_drvdata(ctrl);
+
+ mutex_init(&serport->lock);
+ serport->port = port;
+ serport->tty_idx = idx;
+ serport->tty_drv = drv;
+
+ ctrl->ops = &ctrl_ops;
+
+ ret = serdev_controller_add(ctrl);
+ if (ret)
+ goto err_controller_put;
+
+ dev_info(&ctrl->dev, "tty port %s%d registered\n", drv->name, idx);
+ return &ctrl->dev;
+
+err_controller_put:
+ serdev_controller_put(ctrl);
+ return ERR_PTR(ret);
+}
+
+void serdev_tty_port_unregister(struct tty_port *port)
+{
+ struct serdev_controller *ctrl = port->client_data;
+ struct serport *serport = serdev_controller_get_drvdata(ctrl);
+
+ if (!serport)
+ return;
+
+ serdev_controller_remove(ctrl);
+ port->client_ops = NULL;
+ port->client_data = NULL;
+ serdev_controller_put(ctrl);
+}
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
index 6e8cd6ad0a85..fe7becd881aa 100644
--- a/include/linux/serdev.h
+++ b/include/linux/serdev.h
@@ -210,4 +210,25 @@ static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl,
return serdev->ops->receive_buf(ctrl->serdev, data, count);
}
+/*
+ * serdev hooks into TTY core
+ */
+struct tty_port;
+struct tty_driver;
+
+#ifdef CONFIG_SERIAL_DEV_CTRL_TTYPORT
+struct device *serdev_tty_port_register(struct tty_port *port,
+ struct device *parent,
+ struct tty_driver *drv, int idx);
+void serdev_tty_port_unregister(struct tty_port *port);
+#else
+static inline struct device *serdev_tty_port_register(struct tty_port *port,
+ struct device *parent,
+ struct tty_driver *drv, int idx)
+{
+ return ERR_PTR(-ENODEV);
+}
+static inline void serdev_tty_port_unregister(struct tty_port *port) {}
+#endif /* CONFIG_SERIAL_DEV_CTRL_TTYPORT */
+
#endif /*_LINUX_SERDEV_H */
--
2.10.1
The serdev bus is designed for devices such as Bluetooth, WiFi, GPS
and NFC connected to UARTs on host processors. Tradionally these have
been handled with tty line disciplines, rfkill, and userspace glue such
as hciattach. This approach has many drawbacks since it doesn't fit
into the Linux driver model. Handling of sideband signals, power control
and firmware loading are the main issues.
This creates a serdev bus with controllers (i.e. host serial ports) and
attached devices. Typically, these are point to point connections, but
some devices have muxing protocols or a h/w mux is conceivable. Any
muxing is not yet supported with the serdev bus.
Signed-off-by: Rob Herring <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- Change the sysfs visible names to "serial". Kept serdev as kernel
internal name.
- Make function ptr checks and calls follow a consistent structure
- Return error if controller .open() is missing
- Sort includes
- Add an ACPI TODO note
- Fix serdev_controller_add error path handling
- Fix DT child device adding error handling
- Add modalias sysfs attr and uevent hook
- Fix module email address
MAINTAINERS | 8 +
drivers/char/Kconfig | 1 +
drivers/tty/Makefile | 1 +
drivers/tty/serdev/Kconfig | 8 +
drivers/tty/serdev/Makefile | 3 +
drivers/tty/serdev/core.c | 421 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/serdev.h | 213 ++++++++++++++++++++++
7 files changed, 655 insertions(+)
create mode 100644 drivers/tty/serdev/Kconfig
create mode 100644 drivers/tty/serdev/Makefile
create mode 100644 drivers/tty/serdev/core.c
create mode 100644 include/linux/serdev.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 5f0420a0da5b..dc721d75d2f1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10831,6 +10831,14 @@ S: Maintained
F: Documentation/devicetree/bindings/serial/
F: drivers/tty/serial/
+SERIAL DEVICE BUS
+M: Rob Herring <[email protected]>
+L: [email protected]
+S: Maintained
+F: Documentation/devicetree/bindings/serial/slave-device.txt
+F: drivers/tty/serdev/
+F: include/linux/serdev.h
+
SERIAL IR RECEIVER
M: Sean Young <[email protected]>
L: [email protected]
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index fde005ef9d36..db8f74bbaf3e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -46,6 +46,7 @@ config SGI_MBCS
say Y or M here, otherwise say N.
source "drivers/tty/serial/Kconfig"
+source "drivers/tty/serdev/Kconfig"
config TTY_PRINTK
tristate "TTY driver to output user messages via printk"
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 5817e2397463..b95bed92da9f 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_R3964) += n_r3964.o
obj-y += vt/
obj-$(CONFIG_HVC_DRIVER) += hvc/
obj-y += serial/
+obj-$(CONFIG_SERIAL_DEV_BUS) += serdev/
# tty drivers
obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
diff --git a/drivers/tty/serdev/Kconfig b/drivers/tty/serdev/Kconfig
new file mode 100644
index 000000000000..3b6ecd187bef
--- /dev/null
+++ b/drivers/tty/serdev/Kconfig
@@ -0,0 +1,8 @@
+#
+# Serial bus device driver configuration
+#
+menuconfig SERIAL_DEV_BUS
+ tristate "Serial device bus"
+ help
+ Core support for devices connected via a serial port.
+
diff --git a/drivers/tty/serdev/Makefile b/drivers/tty/serdev/Makefile
new file mode 100644
index 000000000000..01a9b62183f4
--- /dev/null
+++ b/drivers/tty/serdev/Makefile
@@ -0,0 +1,3 @@
+serdev-objs := core.o
+
+obj-$(CONFIG_SERIAL_DEV_BUS) += serdev.o
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
new file mode 100644
index 000000000000..a77842ff0217
--- /dev/null
+++ b/drivers/tty/serdev/core.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd., Rob Herring <[email protected]>
+ *
+ * Based on drivers/spmi/spmi.c:
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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 for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/serdev.h>
+#include <linux/slab.h>
+
+static bool is_registered;
+static DEFINE_IDA(ctrl_ida);
+
+static void serdev_device_release(struct device *dev)
+{
+ struct serdev_device *serdev = to_serdev_device(dev);
+ kfree(serdev);
+}
+
+static const struct device_type serdev_device_type = {
+ .release = serdev_device_release,
+};
+
+static void serdev_ctrl_release(struct device *dev)
+{
+ struct serdev_controller *ctrl = to_serdev_controller(dev);
+ ida_simple_remove(&ctrl_ida, ctrl->nr);
+ kfree(ctrl);
+}
+
+static const struct device_type serdev_ctrl_type = {
+ .release = serdev_ctrl_release,
+};
+
+static int serdev_device_match(struct device *dev, struct device_driver *drv)
+{
+ /* TODO: ACPI and platform matching */
+ return of_driver_match_device(dev, drv);
+}
+
+static int serdev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ /* TODO: ACPI and platform modalias */
+ return of_device_uevent_modalias(dev, env);
+}
+
+/**
+ * serdev_device_add() - add a device previously constructed via serdev_device_alloc()
+ * @serdev: serdev_device to be added
+ */
+int serdev_device_add(struct serdev_device *serdev)
+{
+ struct device *parent = serdev->dev.parent;
+ int err;
+
+ dev_set_name(&serdev->dev, "%s-%d", dev_name(parent), serdev->nr);
+
+ err = device_add(&serdev->dev);
+ if (err < 0) {
+ dev_err(&serdev->dev, "Can't add %s, status %d\n",
+ dev_name(&serdev->dev), err);
+ goto err_device_add;
+ }
+
+ dev_dbg(&serdev->dev, "device %s registered\n", dev_name(&serdev->dev));
+
+err_device_add:
+ return err;
+}
+EXPORT_SYMBOL_GPL(serdev_device_add);
+
+/**
+ * serdev_device_remove(): remove an serdev device
+ * @serdev: serdev_device to be removed
+ */
+void serdev_device_remove(struct serdev_device *serdev)
+{
+ device_unregister(&serdev->dev);
+}
+EXPORT_SYMBOL_GPL(serdev_device_remove);
+
+int serdev_device_open(struct serdev_device *serdev)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->open)
+ return -EINVAL;
+
+ return ctrl->ops->open(ctrl);
+}
+EXPORT_SYMBOL_GPL(serdev_device_open);
+
+void serdev_device_close(struct serdev_device *serdev)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->close)
+ return;
+
+ ctrl->ops->close(ctrl);
+}
+EXPORT_SYMBOL_GPL(serdev_device_close);
+
+int serdev_device_write_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t count)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->write_buf)
+ return -EINVAL;
+
+ return ctrl->ops->write_buf(ctrl, buf, count);
+}
+EXPORT_SYMBOL_GPL(serdev_device_write_buf);
+
+void serdev_device_write_flush(struct serdev_device *serdev)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->write_flush)
+ return;
+
+ ctrl->ops->write_flush(ctrl);
+}
+EXPORT_SYMBOL_GPL(serdev_device_write_flush);
+
+int serdev_device_write_room(struct serdev_device *serdev)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->write_room)
+ return 0;
+
+ return serdev->ctrl->ops->write_room(ctrl);
+}
+EXPORT_SYMBOL_GPL(serdev_device_write_room);
+
+unsigned int serdev_device_set_baudrate(struct serdev_device *serdev, unsigned int speed)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->set_baudrate)
+ return 0;
+
+ return ctrl->ops->set_baudrate(ctrl, speed);
+
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_baudrate);
+
+void serdev_device_set_flow_control(struct serdev_device *serdev, bool enable)
+{
+ struct serdev_controller *ctrl = serdev->ctrl;
+
+ if (!ctrl || !ctrl->ops->set_flow_control)
+ return;
+
+ ctrl->ops->set_flow_control(ctrl, enable);
+}
+EXPORT_SYMBOL_GPL(serdev_device_set_flow_control);
+
+static int serdev_drv_probe(struct device *dev)
+{
+ const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
+
+ return sdrv->probe(to_serdev_device(dev));
+}
+
+static int serdev_drv_remove(struct device *dev)
+{
+ const struct serdev_device_driver *sdrv = to_serdev_device_driver(dev->driver);
+
+ sdrv->remove(to_serdev_device(dev));
+ return 0;
+}
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t len = of_device_get_modalias(dev, buf, PAGE_SIZE - 2);
+ buf[len] = '\n';
+ buf[len+1] = 0;
+ return len+1;
+}
+
+static struct device_attribute serdev_device_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL
+};
+
+static struct bus_type serdev_bus_type = {
+ .name = "serial",
+ .match = serdev_device_match,
+ .probe = serdev_drv_probe,
+ .remove = serdev_drv_remove,
+ .uevent = serdev_uevent,
+ .dev_attrs = serdev_device_attrs,
+};
+
+/**
+ * serdev_controller_alloc() - Allocate a new serdev device
+ * @ctrl: associated controller
+ *
+ * Caller is responsible for either calling serdev_device_add() to add the
+ * newly allocated controller, or calling serdev_device_put() to discard it.
+ */
+struct serdev_device *serdev_device_alloc(struct serdev_controller *ctrl)
+{
+ struct serdev_device *serdev;
+
+ serdev = kzalloc(sizeof(*serdev), GFP_KERNEL);
+ if (!serdev)
+ return NULL;
+
+ serdev->ctrl = ctrl;
+ ctrl->serdev = serdev;
+ device_initialize(&serdev->dev);
+ serdev->dev.parent = &ctrl->dev;
+ serdev->dev.bus = &serdev_bus_type;
+ serdev->dev.type = &serdev_device_type;
+ return serdev;
+}
+EXPORT_SYMBOL_GPL(serdev_device_alloc);
+
+/**
+ * serdev_controller_alloc() - Allocate a new serdev controller
+ * @parent: parent device
+ * @size: size of private data
+ *
+ * Caller is responsible for either calling serdev_controller_add() to add the
+ * newly allocated controller, or calling serdev_controller_put() to discard it.
+ * The allocated private data region may be accessed via
+ * serdev_controller_get_drvdata()
+ */
+struct serdev_controller *serdev_controller_alloc(struct device *parent,
+ size_t size)
+{
+ struct serdev_controller *ctrl;
+ int id;
+
+ if (WARN_ON(!parent))
+ return NULL;
+
+ ctrl = kzalloc(sizeof(*ctrl) + size, GFP_KERNEL);
+ if (!ctrl)
+ return NULL;
+
+ device_initialize(&ctrl->dev);
+ ctrl->dev.type = &serdev_ctrl_type;
+ ctrl->dev.bus = &serdev_bus_type;
+ ctrl->dev.parent = parent;
+ ctrl->dev.of_node = parent->of_node;
+ serdev_controller_set_drvdata(ctrl, &ctrl[1]);
+
+ id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(parent,
+ "unable to allocate serdev controller identifier.\n");
+ serdev_controller_put(ctrl);
+ return NULL;
+ }
+
+ ctrl->nr = id;
+ dev_set_name(&ctrl->dev, "serial%d", id);
+
+ dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
+ return ctrl;
+}
+EXPORT_SYMBOL_GPL(serdev_controller_alloc);
+
+static int of_serdev_register_devices(struct serdev_controller *ctrl)
+{
+ struct device_node *node;
+ struct serdev_device *serdev = NULL;
+ int err;
+ bool found = false;
+
+ for_each_available_child_of_node(ctrl->dev.of_node, node) {
+ if (!of_get_property(node, "compatible", NULL))
+ continue;
+
+ dev_dbg(&ctrl->dev, "adding child %s\n", node->full_name);
+
+ serdev = serdev_device_alloc(ctrl);
+ if (!serdev)
+ continue;
+
+ serdev->dev.of_node = node;
+
+ err = serdev_device_add(serdev);
+ if (err) {
+ dev_err(&serdev->dev,
+ "failure adding device. status %d\n", err);
+ serdev_device_put(serdev);
+ } else
+ found = true;
+ }
+ if (!found)
+ return -ENODEV;
+
+ return 0;
+}
+
+/**
+ * serdev_controller_add() - Add an serdev controller
+ * @ctrl: controller to be registered.
+ *
+ * Register a controller previously allocated via serdev_controller_alloc() with
+ * the serdev core.
+ */
+int serdev_controller_add(struct serdev_controller *ctrl)
+{
+ int ret;
+
+ /* Can't register until after driver model init */
+ if (WARN_ON(!is_registered))
+ return -EAGAIN;
+
+ ret = device_add(&ctrl->dev);
+ if (ret)
+ return ret;
+
+ ret = of_serdev_register_devices(ctrl);
+ if (ret)
+ goto out_dev_del;
+
+ dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n",
+ ctrl->nr, &ctrl->dev);
+ return 0;
+
+out_dev_del:
+ device_del(&ctrl->dev);
+ return ret;
+};
+EXPORT_SYMBOL_GPL(serdev_controller_add);
+
+/* Remove a device associated with a controller */
+static int serdev_remove_device(struct device *dev, void *data)
+{
+ struct serdev_device *serdev = to_serdev_device(dev);
+ if (dev->type == &serdev_device_type)
+ serdev_device_remove(serdev);
+ return 0;
+}
+
+/**
+ * serdev_controller_remove(): remove an serdev controller
+ * @ctrl: controller to remove
+ *
+ * Remove a serdev controller. Caller is responsible for calling
+ * serdev_controller_put() to discard the allocated controller.
+ */
+void serdev_controller_remove(struct serdev_controller *ctrl)
+{
+ int dummy;
+
+ if (!ctrl)
+ return;
+
+ dummy = device_for_each_child(&ctrl->dev, NULL,
+ serdev_remove_device);
+ device_del(&ctrl->dev);
+}
+EXPORT_SYMBOL_GPL(serdev_controller_remove);
+
+/**
+ * serdev_driver_register() - Register client driver with serdev core
+ * @sdrv: client driver to be associated with client-device.
+ *
+ * This API will register the client driver with the serdev framework.
+ * It is typically called from the driver's module-init function.
+ */
+int __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct module *owner)
+{
+ sdrv->driver.bus = &serdev_bus_type;
+ sdrv->driver.owner = owner;
+
+ /* force drivers to async probe so I/O is possible in probe */
+ sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS;
+
+ return driver_register(&sdrv->driver);
+}
+EXPORT_SYMBOL_GPL(__serdev_device_driver_register);
+
+static void __exit serdev_exit(void)
+{
+ bus_unregister(&serdev_bus_type);
+}
+module_exit(serdev_exit);
+
+static int __init serdev_init(void)
+{
+ int ret;
+
+ ret = bus_register(&serdev_bus_type);
+ if (ret)
+ return ret;
+
+ is_registered = true;
+ return 0;
+}
+/* Must be before serial drivers register */
+postcore_initcall(serdev_init);
+
+MODULE_AUTHOR("Rob Herring <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Serial attached device bus");
diff --git a/include/linux/serdev.h b/include/linux/serdev.h
new file mode 100644
index 000000000000..6e8cd6ad0a85
--- /dev/null
+++ b/include/linux/serdev.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 Linaro Ltd., Rob Herring <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 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 for more details.
+ */
+#ifndef _LINUX_SERDEV_H
+#define _LINUX_SERDEV_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+struct serdev_controller;
+struct serdev_device;
+
+/*
+ * serdev device functions
+ */
+
+/**
+ * struct serdev_device_ops - Callback operations for a serdev device
+ * @receive_buf: Function called with data received from device.
+ * @write_wakeup: Function called when ready to transmit more data.
+ */
+struct serdev_device_ops {
+ int (*receive_buf)(struct serdev_device *, const unsigned char *, size_t);
+ void (*write_wakeup)(struct serdev_device *);
+};
+
+/**
+ * struct serdev_device - Basic representation of an serdev device
+ * @dev: Driver model representation of the device.
+ * @nr: Device number on serdev bus.
+ * @ctrl: serdev controller managing this device.
+ * @ops: Device operations.
+ */
+struct serdev_device {
+ struct device dev;
+ int nr;
+ struct serdev_controller *ctrl;
+ const struct serdev_device_ops *ops;
+};
+
+static inline struct serdev_device *to_serdev_device(struct device *d)
+{
+ return container_of(d, struct serdev_device, dev);
+}
+
+static inline void *serdev_device_get_drvdata(const struct serdev_device *serdev)
+{
+ return dev_get_drvdata(&serdev->dev);
+}
+
+static inline void serdev_device_set_drvdata(struct serdev_device *serdev, void *data)
+{
+ dev_set_drvdata(&serdev->dev, data);
+}
+
+/**
+ * serdev_device_put() - decrement serdev device refcount
+ * @serdev serdev device.
+ */
+static inline void serdev_device_put(struct serdev_device *serdev)
+{
+ if (serdev)
+ put_device(&serdev->dev);
+}
+
+struct serdev_device *serdev_device_alloc(struct serdev_controller *);
+int serdev_device_add(struct serdev_device *);
+void serdev_device_remove(struct serdev_device *);
+
+static inline void serdev_device_set_client_ops(struct serdev_device *serdev,
+ const struct serdev_device_ops *ops)
+{
+ serdev->ops = ops;
+}
+
+/*
+ * serdev device driver functions
+ */
+
+/**
+ * struct serdev_device_driver - serdev slave device driver
+ * @driver: serdev device drivers should initialize name field of this
+ * structure.
+ * @probe: binds this driver to a serdev device.
+ * @remove: unbinds this driver from the serdev device.
+ */
+struct serdev_device_driver {
+ struct device_driver driver;
+ int (*probe)(struct serdev_device *);
+ void (*remove)(struct serdev_device *);
+};
+
+static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d)
+{
+ return container_of(d, struct serdev_device_driver, driver);
+}
+
+int __serdev_device_driver_register(struct serdev_device_driver *, struct module *);
+#define serdev_device_driver_register(sdrv) \
+ __serdev_device_driver_register(sdrv, THIS_MODULE)
+
+/**
+ * serdev_device_driver_unregister() - unregister an serdev client driver
+ * @sdrv: the driver to unregister
+ */
+static inline void serdev_device_driver_unregister(struct serdev_device_driver *sdrv)
+{
+ if (sdrv)
+ driver_unregister(&sdrv->driver);
+}
+
+#define module_serdev_device_driver(__serdev_device_driver) \
+ module_driver(__serdev_device_driver, serdev_device_driver_register, \
+ serdev_device_driver_unregister)
+
+int serdev_device_open(struct serdev_device *);
+void serdev_device_close(struct serdev_device *);
+unsigned int serdev_device_set_baudrate(struct serdev_device *, unsigned int);
+void serdev_device_set_flow_control(struct serdev_device *, bool);
+int serdev_device_write_buf(struct serdev_device *, const unsigned char *, size_t);
+void serdev_device_write_flush(struct serdev_device *);
+int serdev_device_write_room(struct serdev_device *);
+
+/*
+ * serdev controller functions
+ */
+struct serdev_controller_ops {
+ int (*write_buf)(struct serdev_controller *, const unsigned char *, size_t);
+ void (*write_flush)(struct serdev_controller *);
+ int (*write_room)(struct serdev_controller *);
+ int (*open)(struct serdev_controller *);
+ void (*close)(struct serdev_controller *);
+ void (*set_flow_control)(struct serdev_controller *, bool);
+ unsigned int (*set_baudrate)(struct serdev_controller *, unsigned int);
+};
+
+/**
+ * struct serdev_controller - interface to the serdev controller
+ * @dev: Driver model representation of the device.
+ * @nr: number identifier for this controller/bus.
+ * @serdev: Pointer to slave device for this controller.
+ * @ops: Controller operations.
+ */
+struct serdev_controller {
+ struct device dev;
+ unsigned int nr;
+ struct serdev_device *serdev;
+ const struct serdev_controller_ops *ops;
+};
+
+static inline struct serdev_controller *to_serdev_controller(struct device *d)
+{
+ return container_of(d, struct serdev_controller, dev);
+}
+
+static inline
+void *serdev_controller_get_drvdata(const struct serdev_controller *ctrl)
+{
+ return ctrl ? dev_get_drvdata(&ctrl->dev) : NULL;
+}
+
+static inline void serdev_controller_set_drvdata(struct serdev_controller *ctrl,
+ void *data)
+{
+ dev_set_drvdata(&ctrl->dev, data);
+}
+
+/**
+ * serdev_controller_put() - decrement controller refcount
+ * @ctrl serdev controller.
+ */
+static inline void serdev_controller_put(struct serdev_controller *ctrl)
+{
+ if (ctrl)
+ put_device(&ctrl->dev);
+}
+
+struct serdev_controller *serdev_controller_alloc(struct device *, size_t);
+int serdev_controller_add(struct serdev_controller *);
+void serdev_controller_remove(struct serdev_controller *);
+
+static inline void serdev_controller_write_wakeup(struct serdev_controller *ctrl)
+{
+ struct serdev_device *serdev = ctrl->serdev;
+
+ if (!serdev || !serdev->ops->write_wakeup)
+ return;
+
+ serdev->ops->write_wakeup(ctrl->serdev);
+}
+
+static inline int serdev_controller_receive_buf(struct serdev_controller *ctrl,
+ const unsigned char *data,
+ size_t count)
+{
+ struct serdev_device *serdev = ctrl->serdev;
+
+ if (!serdev || !serdev->ops->receive_buf)
+ return -EINVAL;
+
+ return serdev->ops->receive_buf(ctrl->serdev, data, count);
+}
+
+#endif /*_LINUX_SERDEV_H */
--
2.10.1
Add a common binding for describing serial/UART attached devices. Common
examples are Bluetooth, WiFi, NFC and GPS devices.
Serial attached devices are represented as child nodes of a UART node.
This may need to be extended for more complex devices with multiple
interfaces, but for the simple cases a child node is sufficient.
Signed-off-by: Rob Herring <[email protected]>
---
v2:
- Drop reg property
- Add max-speed property
.../devicetree/bindings/serial/slave-device.txt | 36 ++++++++++++++++++++++
1 file changed, 36 insertions(+)
create mode 100644 Documentation/devicetree/bindings/serial/slave-device.txt
diff --git a/Documentation/devicetree/bindings/serial/slave-device.txt b/Documentation/devicetree/bindings/serial/slave-device.txt
new file mode 100644
index 000000000000..f66037928f5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/serial/slave-device.txt
@@ -0,0 +1,36 @@
+Serial Slave Device DT binding
+
+This documents the binding structure and common properties for serial
+attached devices. Common examples include Bluetooth, WiFi, NFC and GPS
+devices.
+
+Serial attached devices shall be a child node of the host UART device the
+slave device is attached to. It is expected that the attached device is
+the only child node of the UART device. The slave device node name shall
+reflect the generic type of device for the node.
+
+Required Properties:
+
+- compatible : A string reflecting the vendor and specific device the node
+ represents.
+
+Optional Properties:
+
+- max-speed : The maximum baud rate the device operates at. This should
+ only be present if the maximum is less than the slave device
+ can support. For example, a particular board has some signal
+ quality issue or the host processor can't support higher
+ baud rates.
+
+Example:
+
+serial@1234 {
+ compatible = "ns16550a";
+ interrupts = <1>;
+
+ bluetooth {
+ compatible = "brcm,bcm43341-bt";
+ interrupt-parent = <&gpio>;
+ interrupts = <10>;
+ };
+};
--
2.10.1
Introduce a client (upward direction) operations struct for tty_port
clients. Initially supported operations are for receiving data and write
wake-up. This will allow for having clients other than an ldisc.
Convert the calls to the ldisc to use the client ops as the default
operations.
Signed-off-by: Rob Herring <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- no change
drivers/tty/tty_buffer.c | 17 +++--------------
drivers/tty/tty_port.c | 39 ++++++++++++++++++++++++++++++++++++++-
include/linux/tty.h | 9 ++++++++-
3 files changed, 49 insertions(+), 16 deletions(-)
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index f4dc3e296dd5..4e7a4e9dcf4d 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -437,7 +437,7 @@ int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf);
static int
-receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count)
+receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
{
unsigned char *p = char_buf_ptr(head, head->read);
char *f = NULL;
@@ -445,7 +445,7 @@ receive_buf(struct tty_ldisc *ld, struct tty_buffer *head, int count)
if (~head->flags & TTYB_NORMAL)
f = flag_buf_ptr(head, head->read);
- return tty_ldisc_receive_buf(ld, p, f, count);
+ return port->client_ops->receive_buf(port, p, f, count);
}
/**
@@ -465,16 +465,6 @@ static void flush_to_ldisc(struct work_struct *work)
{
struct tty_port *port = container_of(work, struct tty_port, buf.work);
struct tty_bufhead *buf = &port->buf;
- struct tty_struct *tty;
- struct tty_ldisc *disc;
-
- tty = READ_ONCE(port->itty);
- if (tty == NULL)
- return;
-
- disc = tty_ldisc_ref(tty);
- if (disc == NULL)
- return;
mutex_lock(&buf->lock);
@@ -504,7 +494,7 @@ static void flush_to_ldisc(struct work_struct *work)
continue;
}
- count = receive_buf(disc, head, count);
+ count = receive_buf(port, head, count);
if (!count)
break;
head->read += count;
@@ -512,7 +502,6 @@ static void flush_to_ldisc(struct work_struct *work)
mutex_unlock(&buf->lock);
- tty_ldisc_deref(disc);
}
/**
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 1d8804843103..232a8cbf47bc 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -17,6 +17,41 @@
#include <linux/delay.h>
#include <linux/module.h>
+static int tty_port_default_receive_buf(struct tty_port *port,
+ const unsigned char *p,
+ const unsigned char *f, size_t count)
+{
+ int ret;
+ struct tty_struct *tty;
+ struct tty_ldisc *disc;
+
+ tty = READ_ONCE(port->itty);
+ if (!tty)
+ return 0;
+
+ disc = tty_ldisc_ref(tty);
+ if (!disc)
+ return 0;
+
+ ret = tty_ldisc_receive_buf(disc, p, (char *)f, count);
+
+ tty_ldisc_deref(disc);
+
+ return ret;
+}
+
+static void tty_port_default_wakeup(struct tty_port *port)
+{
+ /* tty_port_tty_wakeup already took a reference to the tty */
+ tty_wakeup(port->tty);
+}
+
+static const struct tty_port_client_operations default_client_ops = {
+ .receive_buf = tty_port_default_receive_buf,
+ .write_wakeup = tty_port_default_wakeup,
+};
+
+
void tty_port_init(struct tty_port *port)
{
memset(port, 0, sizeof(*port));
@@ -28,6 +63,7 @@ void tty_port_init(struct tty_port *port)
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
+ port->client_ops = &default_client_ops;
kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);
@@ -275,7 +311,8 @@ void tty_port_tty_wakeup(struct tty_port *port)
struct tty_struct *tty = tty_port_tty_get(port);
if (tty) {
- tty_wakeup(tty);
+ if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags))
+ port->client_ops->write_wakeup(port);
tty_kref_put(tty);
}
}
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 21c0fabfed60..1017e904c0a3 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -217,12 +217,18 @@ struct tty_port_operations {
/* Called on the final put of a port */
void (*destruct)(struct tty_port *port);
};
-
+
+struct tty_port_client_operations {
+ int (*receive_buf)(struct tty_port *port, const unsigned char *, const unsigned char *, size_t);
+ void (*write_wakeup)(struct tty_port *port);
+};
+
struct tty_port {
struct tty_bufhead buf; /* Locked internally */
struct tty_struct *tty; /* Back pointer */
struct tty_struct *itty; /* internal back ptr */
const struct tty_port_operations *ops; /* Port operations */
+ const struct tty_port_client_operations *client_ops; /* Port client operations */
spinlock_t lock; /* Lock protecting tty field */
int blocked_open; /* Waiting to open */
int count; /* Usage count */
@@ -241,6 +247,7 @@ struct tty_port {
based drain is needed else
set to size of fifo */
struct kref kref; /* Ref counter */
+ void *client_data;
};
/* tty_port::iflags bits -- use atomic bit ops */
--
2.10.1
This is needed to work with the client operations which uses const ptrs.
Really, the flags pointer could be const, too, but this would be a tree
wide fix.
Signed-off-by: Rob Herring <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- no change
drivers/tty/tty_buffer.c | 2 +-
include/linux/tty.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c
index aa80dc94ddc2..f4dc3e296dd5 100644
--- a/drivers/tty/tty_buffer.c
+++ b/drivers/tty/tty_buffer.c
@@ -422,7 +422,7 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
*
* Returns the number of bytes not processed
*/
-int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
+int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
char *f, int count)
{
if (ld->ops->receive_buf2)
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 86c7853282b7..21c0fabfed60 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -657,7 +657,7 @@ extern int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty);
extern void tty_ldisc_release(struct tty_struct *tty);
extern void tty_ldisc_init(struct tty_struct *tty);
extern void tty_ldisc_deinit(struct tty_struct *tty);
-extern int tty_ldisc_receive_buf(struct tty_ldisc *ld, unsigned char *p,
+extern int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
char *f, int count);
/* n_tty.c */
--
2.10.1
tty_register_device is just a wrapper for tty_register_device_attr with
NULL passed for drvdata and attr_grp. So similarly make
tty_port_register_device a wrapper of tty_port_register_device_attr so that
additions don't have to be made in both functions.
Signed-off-by: Rob Herring <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- no change
drivers/tty/tty_port.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index 606d9e5bf28f..1d8804843103 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -67,8 +67,7 @@ struct device *tty_port_register_device(struct tty_port *port,
struct tty_driver *driver, unsigned index,
struct device *device)
{
- tty_port_link_device(port, driver, index);
- return tty_register_device(driver, index, device);
+ return tty_port_register_device_attr(port, driver, index, device, NULL, NULL);
}
EXPORT_SYMBOL_GPL(tty_port_register_device);
--
2.10.1
From: Alan Cox <[email protected]>
Let us create tty objects entirely in kernel space. Untested proposal to
show why all the ideas around rewriting half the uart stack are not needed.
With this a kernel created non file backed tty object could be used to handle
data, and set terminal modes. Not all ldiscs can cope with this as N_TTY in
particular has to work back to the fs/tty layer.
The tty_port code is however otherwise clean of file handles as far as I can
tell as is the low level tty port write path used by the ldisc, the
configuration low level interfaces and most of the ldiscs.
Currently you don't have any exposure to see tty hangups because those are
built around the file layer. However a) it's a fixed port so you probably
don't care about that b) if you do we can add a callback and c) you almost
certainly don't want the userspace tear down/rebuild behaviour anyway.
This should however be sufficient if we wanted for example to enumerate all
the bluetooth bound fixed ports via ACPI and make them directly available.
It doesn't deal with the case of a user opening a port that's also kernel
opened and that would need some locking out (so it returned EBUSY if bound
to a kernel device of some kind). That needs resolving along with how you
"up" or "down" your new bluetooth device, or enumerate it while providing
the existing tty API to avoid regressions (and to debug).
Alan
Reviewed-by: Andy Shevchenko <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
Alan, Need your SoB here.
v2:
- no change
drivers/tty/tty_io.c | 2 +-
drivers/tty/tty_port.c | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 4790c0fb5a45..a1fd3f7d487a 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -855,7 +855,7 @@ static void tty_vhangup_session(struct tty_struct *tty)
int tty_hung_up_p(struct file *filp)
{
- return (filp->f_op == &hung_up_tty_fops);
+ return (filp && filp->f_op == &hung_up_tty_fops);
}
EXPORT_SYMBOL(tty_hung_up_p);
diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c
index c3f9d93ba227..606d9e5bf28f 100644
--- a/drivers/tty/tty_port.c
+++ b/drivers/tty/tty_port.c
@@ -335,7 +335,7 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts);
* tty_port_block_til_ready - Waiting logic for tty open
* @port: the tty port being opened
* @tty: the tty device being bound
- * @filp: the file pointer of the opener
+ * @filp: the file pointer of the opener or NULL
*
* Implement the core POSIX/SuS tty behaviour when opening a tty device.
* Handles:
@@ -369,7 +369,7 @@ int tty_port_block_til_ready(struct tty_port *port,
tty_port_set_active(port, 1);
return 0;
}
- if (filp->f_flags & O_NONBLOCK) {
+ if (filp == NULL || (filp->f_flags & O_NONBLOCK)) {
/* Indicate we are open */
if (C_BAUD(tty))
tty_port_raise_dtr_rts(port);
--
2.10.1
For in-kernel tty users, we need to be able to create and destroy
'struct tty' that are not associated with a file. The creation side is
fine, but tty_release() needs to be split into the file handle portion
and the struct tty portion. Introduce a new function, tty_release_struct,
to handle just the destroying of a struct tty.
Signed-off-by: Rob Herring <[email protected]>
Reviewed-by: Andy Shevchenko <[email protected]>
Reviewed-By: Sebastian Reichel <[email protected]>
---
v2:
- Add kerneldoc for tty_release_struct
drivers/tty/tty_io.c | 50 ++++++++++++++++++++++++++++++++------------------
include/linux/tty.h | 1 +
2 files changed, 33 insertions(+), 18 deletions(-)
diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c
index 734a635e7363..4790c0fb5a45 100644
--- a/drivers/tty/tty_io.c
+++ b/drivers/tty/tty_io.c
@@ -1745,6 +1745,37 @@ static int tty_release_checks(struct tty_struct *tty, int idx)
}
/**
+ * tty_release_struct - release a tty struct
+ * @tty: tty device
+ * @idx: index of the tty
+ *
+ * Performs the final steps to release and free a tty device. It is
+ * roughly the reverse of tty_init_dev.
+ */
+void tty_release_struct(struct tty_struct *tty, int idx)
+{
+ /*
+ * Ask the line discipline code to release its structures
+ */
+ tty_ldisc_release(tty);
+
+ /* Wait for pending work before tty destruction commmences */
+ tty_flush_works(tty);
+
+ tty_debug_hangup(tty, "freeing structure\n");
+ /*
+ * The release_tty function takes care of the details of clearing
+ * the slots and preserving the termios structure. The tty_unlock_pair
+ * should be safe as we keep a kref while the tty is locked (so the
+ * unlock never unlocks a freed tty).
+ */
+ mutex_lock(&tty_mutex);
+ release_tty(tty, idx);
+ mutex_unlock(&tty_mutex);
+}
+EXPORT_SYMBOL_GPL(tty_release_struct);
+
+/**
* tty_release - vfs callback for close
* @inode: inode of tty
* @filp: file pointer for handle to tty
@@ -1898,25 +1929,8 @@ int tty_release(struct inode *inode, struct file *filp)
return 0;
tty_debug_hangup(tty, "final close\n");
- /*
- * Ask the line discipline code to release its structures
- */
- tty_ldisc_release(tty);
-
- /* Wait for pending work before tty destruction commmences */
- tty_flush_works(tty);
-
- tty_debug_hangup(tty, "freeing structure\n");
- /*
- * The release_tty function takes care of the details of clearing
- * the slots and preserving the termios structure. The tty_unlock_pair
- * should be safe as we keep a kref while the tty is locked (so the
- * unlock never unlocks a freed tty).
- */
- mutex_lock(&tty_mutex);
- release_tty(tty, idx);
- mutex_unlock(&tty_mutex);
+ tty_release_struct(tty, idx);
return 0;
}
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 40144f382516..86c7853282b7 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -528,6 +528,7 @@ extern int tty_alloc_file(struct file *file);
extern void tty_add_file(struct tty_struct *tty, struct file *file);
extern void tty_free_file(struct file *file);
extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx);
+extern void tty_release_struct(struct tty_struct *tty, int idx);
extern int tty_release(struct inode *inode, struct file *filp);
extern void tty_init_termios(struct tty_struct *tty);
extern int tty_standard_install(struct tty_driver *driver,
--
2.10.1