[U-Boot] [RFC PATCH v2 5/8] net: Add basic driver model support to Ethernet stack
Joe Hershberger
joe.hershberger at gmail.com
Sat Feb 14 03:43:46 CET 2015
Hi Simon,
On Thu, Feb 12, 2015 at 11:20 PM, Simon Glass <sjg at chromium.org> wrote:
>
> Hi Joe,
>
> On 10 February 2015 at 23:25, Joe Hershberger <joe.hershberger at gmail.com>
wrote:
> >
> >
> > On Fri, Feb 6, 2015 at 7:25 PM, Simon Glass <sjg at chromium.org> wrote:
> >>
> >> Hi Joe,
> >>
> >> On 2 February 2015 at 17:38, Joe Hershberger <joe.hershberger at ni.com>
> >> wrote:
> >> > First just add support for MAC drivers.
> >> >
> >> > Signed-off-by: Joe Hershberger <joe.hershberger at ni.com>
> >> >
> >> > ---
> >> >
> >> > Changes in v2:
> >> > -Updated comments
> >> > -Removed extra parentheses
> >> > -Changed eth_uclass_priv local var names to be uc_priv
> >> > -Update error codes
> >> > -Cause an invalid name to fail binding
> >> > -Rebase on top of dm/master
> >> > -Stop maintaining our own index and use DM seq now that it works for
our
> >> > needs
> >> > -Move the hwaddr to platdata so that its memory is allocated at bind
> >> > when we need it
> >> > -Prevent device from being probed before used by a command (i.e.
before
> >> > eth_init()).
> >> >
> >> > common/board_r.c | 4 +-
> >> > common/cmd_bdinfo.c | 2 +
> >> > include/dm/uclass-id.h | 1 +
> >> > include/net.h | 24 ++++
> >> > net/eth.c | 319
> >> > ++++++++++++++++++++++++++++++++++++++++++++++++-
> >> > 5 files changed, 346 insertions(+), 4 deletions(-)
> >> >
>
> [snip]
>
> >> > diff --git a/net/eth.c b/net/eth.c
> >> > index c02548c..1b5a169 100644
> >> > --- a/net/eth.c
> >> > +++ b/net/eth.c
> >> > @@ -72,6 +72,320 @@ static int eth_mac_skip(int index)
> >> > return ((skip_state = getenv(enetvar)) != NULL);
> >> > }
> >> >
> >> > +static void eth_current_changed(void);
> >> > +
> >> > +#ifdef CONFIG_DM_ETH
> >> > +#include <dm.h>
> >> > +#include <dm/device-internal.h>
> >>
> >> These should go at the top of the file - should be OK to always
> >> include them (i.e. no #ifdef)
> >
> > OK... Moved them.
> >
> >> > +
> >> > +struct eth_device_priv {
> >> > + int state;
> >> > + void *priv;
> >> > +};
> >> > +
> >> > +struct eth_uclass_priv {
> >> > + struct udevice *current;
> >> > +};
> >> > +
> >> > +static void eth_set_current_to_next(void)
> >> > +{
> >> > + struct uclass *uc;
> >> > + struct eth_uclass_priv *uc_priv;
> >> > +
> >> > + uclass_get(UCLASS_ETH, &uc);
> >>
> >> This can actually return an error, although I agree there is little
> >> point in handling it. So I suppose this is OK.
> >
> > I think this should be a given.
> >
> >> > + uc_priv = uc->priv;
> >> > + uclass_next_device(&uc_priv->current);
> >> > + if (!uc_priv->current)
> >> > + uclass_first_device(UCLASS_ETH, &uc_priv->current);
> >> > +}
> >> > +
> >> > +struct udevice *eth_get_dev(void)
> >> > +{
> >> > + struct uclass *uc;
> >>
> >> blank line here
> >
> > OK on all of these.
> >
> >
> >> > + uclass_get(UCLASS_ETH, &uc);
> >> > +
> >> > + struct eth_uclass_priv *uc_priv = uc->priv;
> >> > + return uc_priv->current;
> >> > +}
> >> > +
> >> > +static void eth_set_dev(struct udevice *dev)
> >> > +{
> >> > + struct uclass *uc;
> >>
> >> blank line here
> >>
> >> > + uclass_get(UCLASS_ETH, &uc);
> >> > +
> >> > + struct eth_uclass_priv *uc_priv = uc->priv;
> >> > + uc_priv->current = dev;
> >> > +}
> >> > +
> >> > +unsigned char *eth_get_ethaddr(void)
> >> > +{
> >> > + struct eth_pdata *pdata;
> >>
> >> blank line here
> >>
> >> > + if (eth_get_dev()) {
> >> > + pdata = eth_get_dev()->platdata;
> >> > + if (pdata)
> >> > + return pdata->enetaddr;
> >> > + }
> >> > + return NULL;
> >> > +}
> >> > +
> >> > +/* Set active state */
> >> > +int eth_init_state_only(bd_t *bis)
> >> > +{
> >> > + struct eth_device_priv *priv;
> >>
> >> blank line here
> >>
> >> > + if (eth_get_dev()) {
> >> > + priv = eth_get_dev()->uclass_priv;
> >> > + if (priv)
> >> > + priv->state = ETH_STATE_ACTIVE;
> >> > + }
> >> > +
> >> > + return 0;
> >> > +}
> >> > +/* Set passive state */
> >> > +void eth_halt_state_only(void)
> >> > +{
> >> > + struct eth_device_priv *priv;
> >>
> >> blank line here
> >>
> >> > + if (eth_get_dev()) {
> >> > + priv = eth_get_dev()->uclass_priv;
> >> > + if (priv)
> >> > + priv->state = ETH_STATE_PASSIVE;
> >> > + }
> >> > +}
> >> > +
> >> > +int eth_get_dev_index(void)
> >> > +{
> >> > + if (eth_get_dev())
> >> > + return eth_get_dev()->seq;
> >> > + return -1;
> >> > +}
> >> > +
> >> > +int eth_init(bd_t *bis)
> >> > +{
> >> > + struct udevice *current, *old_current, *dev;
> >> > + struct uclass *uc;
> >> > +
> >> > + current = eth_get_dev();
> >> > + if (!current) {
> >> > + puts("No ethernet found.\n");
> >> > + return -1;
> >> > + }
> >> > + device_probe(current);
> >>
> >> Check error
> >
> > OK.
> >
> >> > +
> >> > + /* Sync environment with network devices */
> >> > + uclass_get(UCLASS_ETH, &uc);
> >> > + uclass_foreach_dev(dev, uc) {
> >> > + uchar env_enetaddr[6];
> >> > +
> >> > + if (eth_getenv_enetaddr_by_index("eth", dev->seq,
> >> > + env_enetaddr)) {
> >> > + struct eth_pdata *pdata = dev->platdata;
> >>
> >> blank line
> >>
> >> Do all devices have the same platdata by design? What if a particular
> >> device wants its own?
> >
> > That's a good question. I imagine some devices may have something
unique,
> > but I would expect they would read that data into the priv data that
they
> > define. How do other subsystems handle platform data for unique
devices?
>
> There isn't great support for it. Typically the device has its own
> platform data. For buses there is per-child platform data, which the
> uclass can define, but we don't have uclass-defined platform data for
> normal devices (which are not children).
So I guess that means we should leave it until needed and then extend core
to support such things on non-bus-children?
> >
> >> > + if (pdata)
> >>
> >> What do you need this check?
> >
> > No. This was left over from when enetaddr was in priv.
> >
> >
> >> > + memcpy(pdata->enetaddr, env_enetaddr,
> >> > 6);
> >> > + }
> >> > + };
> >> > +
> >> > + old_current = current;
> >> > + do {
> >> > + debug("Trying %s\n", current->name);
> >> > +
> >> > + if (current->driver) {
> >> > + const struct eth_ops *ops =
> >> > current->driver->ops;
> >>
> >> blank line
> >>
> >> > + if (ops->init(current, bis) >= 0) {
> >> > + struct eth_device_priv *priv =
> >> > + current->uclass_priv;
> >> > + if (priv)
> >> > + priv->state =
ETH_STATE_ACTIVE;
> >> > +
> >> > + return 0;
> >> > + }
> >> > + }
> >> > + debug("FAIL\n");
> >> > +
> >> > + eth_try_another(0);
> >> > + current = eth_get_dev();
> >> > + } while (old_current != current);
> >> > +
> >> > + return -ENODEV;
> >> > +}
> >> > +
> >> > +void eth_halt(void)
> >> > +{
> >> > + struct udevice *current;
> >> > +
> >> > + current = eth_get_dev();
> >> > + if (!current)
> >> > + return;
> >> > + if (!current->driver)
> >> > + return;
> >> > +
> >> > + const struct eth_ops *ops = current->driver->ops;
> >> > + ops->halt(current);
> >> > +
> >> > + struct eth_device_priv *priv = current->uclass_priv;
> >> > + if (priv)
> >> > + priv->state = ETH_STATE_PASSIVE;
> >> > +}
> >> > +
> >> > +int eth_send(void *packet, int length)
> >> > +{
> >> > + struct udevice *current;
> >> > +
> >> > + current = eth_get_dev();
> >> > + if (!current)
> >> > + return -1;
> >> > + if (!current->driver)
> >> > + return -1;
> >>
> >> Seems like eth_get_dev() should return an error code if current or
> >> current->driver is NULL.
> >
> > It's returning a pointer, so I prefer it to be NULL or a valid pointer.
> >
> > Perhaps you meant to say that eth_send() should return an error code?
>
> Well yes that too. But -1 is not a valid error anymore. You need to
> use -EPERM or something. And whatever went wrong with eth_get_dev()
> should really be returned. So you could do:
>
> ret = eth_get_dev(¤t);
> if (ret)
> return ret;
I don't see this as equivalent.
> or if you prefer:
>
> current = eth_get_dev();
> if (IS_ERR(current))
> return PTR_ERR(current);
This makes more sense, but given that eth_get_dev() is not doing anything
other than returning a static global variable, it seems that "NULL" is a
valid return. That's just the value of that variable. Nothing "went
wrong". It seems like it's jumping through hoops to convert NULL into some
error code, return that code as a pointer, then convert it back, when the
test for NULL is far more straightforward in the caller and easier to read.
I completely agree about not returning -1 from eth_send() and already have
that change in for the next version.
Thanks,
-Joe
More information about the U-Boot
mailing list