#include <bits/stdc++.h>
#define _for(i,b,e) for (common_type_t <decltype (b), decltype (e)> i = (b), i ## __end = (e); i < i ## __end; ++ i)
#define FOR(...)    _for (__VA_ARGS__)
#define rep(i,n)    FOR(i, size_t{}, n)
#define ALL(x)      begin (x), end (x)
using namespace std;
using ll = int64_t;
constexpr int64_t inf = 2e18;
constexpr size_t invalid = ~0ull;

auto main () -> int
{
  cin.tie (nullptr);
  ios::sync_with_stdio (false);

  size_t H, W;
  cin >> H >> W;
  vector <vector <char>> s (H, vector <char> (W));
  rep (i, H) rep (j, W) cin >> s[i][j];

  using P = pair <size_t, size_t>;
  vector <P> valid_paths;

  rep (i, H) rep (j, W)
  {
    if (s [i][j] == '.') valid_paths.emplace_back (i, j);
  }

  auto is_path = [&] (size_t y, size_t x)
  {
    return x != invalid && x < W && y != invalid && y < H && s [y][x] == '.';
  };

  auto bfs = [&] (const P & start)
  {
    vector <vector <bool>> visited (H, vector <bool> (W, false));
    queue <pair <P, ll>> q;
    q.emplace (start, 0);
    ll maxcost = 0;
    while (not q.empty ())
    {
      auto now = q.front (); q.pop ();
      P pos;
      ll cost;
      size_t y, x;
      tie (pos, cost) = now;
      tie (y, x) = pos;

      visited [y][x] = true;
      maxcost = max (maxcost, cost);

      if (is_path (y - 1, x) && not visited [y - 1][x])
      {
        q.emplace (P {y - 1, x}, cost + 1);
      }
      if (is_path (y, x - 1) && not visited [y][x - 1])
      {
        q.emplace (P {y, x - 1}, cost + 1);
      }
      if (is_path (y + 1, x) && not visited [y + 1][x])
      {
        q.emplace (P {y + 1, x}, cost + 1);
      }
      if (is_path (y, x + 1) && not visited [y][x + 1])
      {
        q.emplace (P {y, x + 1}, cost + 1);
      }
    }
    return maxcost;
  };

  ll ans = 0;
  for (auto && elem : valid_paths)
  {
    ans = max (ans, bfs (elem));
    if (ans == W + H - 2) break;
  }
  cout << ans << endl;
}
