#include <bits/stdc++.h>
using namespace std;

struct edge{
  long long to, cost;
};

typedef vector<edge> ve;
typedef vector<ve> vve;
typedef vector<long long> vl;
typedef vector<vl> vvl;
typedef pair<int, int> pii;

void readInit(int &H, int &W, vve &graph, vvl &A);
void dijkstra(int s, vve &graph, vl &d);

int main(){
  int H, W, check;
  vve graph(170000);
  vvl minD(401, vl(401));
  cin >> H >> W;
  vvl A(H, vl(W));

  check = 1;
  readInit(H, W, graph, A, check);
  long long ans = 0;
  if(check) cout << 0 << endl;
  else{
    for(int i = 0; i < H; i++){
      for(int j = 0; j < W; j++){
        int index = i*W+j;
        if(A[i][j] == 1) continue;
        for(int k = i; k < H; k++){
          for(int l = 0; l < W; l++){
            int goal = k*W+l;
            if(A[k][l] == 1) continue;
            dijkstra(index, graph, minD[index]);
            long long cost = minD[index][goal];
            ans = max(ans, cost);
          }
        }
      }
    }
    cout << ans << endl;
  }
}
/*++++++++++++++++++++*/
void readInit(int &H, int &W, vve &graph, vvl &A, int check){
  for(int i = 0; i < H; i++){
    for(int j = 0; j < W; j++){
      char tmp;
      cin >> tmp;
      if(tmp == '.') A[i][j] = 0;
      else A[i][j] = 1;
    }
  }
  for(int i = 0; i < H; i++){
    for(int j = 0; j < W; j++){
      if(i == 0){
        if(j == 0){
          if(A[i][j+1] == 0){
            edge e1 = {j+1,1};
            graph[j].push_back(e1);
            check = 0;
          }
          if(A[i+1][j] == 0){
            edge e2 = {W+j,1};
            graph[j].push_back(e2);
            check = 0;
          }
        }
        else if(j == W-1){
          if(A[i][j-1] == 0){
            edge e1 = {j-1,1};
            graph[j].push_back(e1);
            check = 0;
          }
          if(A[i+1][j] == 0){
            edge e2 = {W+j,1};
            graph[j].push_back(e2);
            check = 0;
          }
        }else{
          if(A[i][j-1] == 0){
            edge e1 = {j-1,1};
            graph[j].push_back(e1);
            check = 0;
          }
          if(A[i][j+1] == 0){
            edge e2 = {j+1,1};
            graph[j].push_back(e2);
            check = 0;
          }
          if(A[i+1][j] == 0){
            edge e3 = {W+j,1};
            graph[j].push_back(e3);
            check = 0;
          }
        }
      }else if(i == H-1){
        if(j == 0){
          if(A[i][j+1] == 0){
            edge e1 = {W*i+j+1,1};
            graph[W*i].push_back(e1);
            check = 0;
          }
          if(A[i-1][j] == 0){
            edge e2 = {W*(i-1)+j,1};
            graph[W*i].push_back(e2);
            check = 0;
          }
        }else if(j == W-1){
          if(A[i][j-1] == 0){
            edge e1 = {W*i+j-1,1};
            graph[W*i+j].push_back(e1);
            check = 0;
          }
          if(A[i-1][j] == 0){
            edge e2 = {W*(i-1)+j,1};
            graph[W*i+j].push_back(e2);
            check = 0;
          }
        }else{
          if(A[i][j-1] == 0){
            edge e1 = {W*i+j-1,1};
            graph[W*i+j].push_back(e1);
            check = 0;
          }
          if(A[i][j+1] == 0){
            edge e2 = {W*i+j+1,1};
            graph[W*i+j].push_back(e2);
            check = 0;
          }
          if(A[i-1][j] == 0){
            edge e3 = {W*(i-1)+j,1};
            graph[W*i+j].push_back(e3);
            check = 0;
          }
        }
      }else{
        if(j == 0){
          if(A[i][j+1] == 0){
            edge e1 = {W*i+j+1,1};
            graph[W*i+j].push_back(e1);
            check = 0;
          }
          if(A[i-1][j] == 0){
            edge e2 = {W*(i-1)+j,1};
            graph[W*i+j].push_back(e2);
            check = 0;
          }
          if(A[i+1][j] == 0){
            edge e3 = {W*(i+1)+j,1};
            graph[W*i+j].push_back(e3);
            check = 0;
          }
        }else if(j == W-1){
          if(A[i][j-1] == 0){
            edge e1 = {W*i+j-1,1};
            graph[W*i+j].push_back(e1);
            check = 0;
          }
          if(A[i-1][j] == 0){
            edge e2 = {W*(i-1)+j,1};
            graph[W*i+j].push_back(e2);
            check = 0;
          }
          if(A[i+1][j] == 0){
            edge e3 = {W*(i+1)+j,1};
            graph[W*i+j].push_back(e3);
            check = 0;
          }
        }else{
          if(A[i][j-1] == 0){
            edge e1 = {W*i+j-1,1};
            graph[W*i+j].push_back(e1);
            check = 0;
          }
          if(A[i][j+1] == 0){
            edge e2 = {W*i+j+1,1};
            graph[W*i+j].push_back(e2);
            check = 0;
          }
          if(A[i-1][j] == 0){
            edge e3 = {W*(i-1)+j,1};
            graph[W*i+j].push_back(e3);
            check = 0;
          }
          if(A[i+1][j] == 0){
            edge e4 = {W*(i+1)+j,1};
            graph[W*i+j].push_back(e4);
            check = 0;
          }
        }
      }
    }
  }
}
void dijkstra(int s, vve &graph, vl &d){
  priority_queue <pii, vector<pii>, greater<pii> > que;
  for(int i = 0; i < d.size(); i++) d[i] = 1000000;
  d[s] = 0;
  que.push(pii(0, s));

  while(!que.empty()){
    pii tmpP = que.top();
    que.pop();
    int v = tmpP.second;
    if(d[v] < tmpP.first) continue;
    for(int i = 0; i < graph[v].size(); i++){
      edge e = graph[v][i];
      if(d[e.to] > d[v] + e.cost){
        d[e.to] = d[v] + e.cost;
        que.push(pii(d[e.to], e.to));
      }
    }
  }
}
