using System;
using System.Collections.Generic;
using System.Linq;

class Program {
    static void Main(string[] args) {
        int H, W;
        Input(out H, out W);
        char[][] S = new char[H][];
        for(int i = 0; i < H; i++) {
            string line;
            Input(out line);
            S[i] = new char[W];
            for(int j = 0; j < W; j++) {
                S[i][j] = line[j];
            }
        }
        int n = H * W;
        int[][] graph = new int[n][];
        for(int y = 0; y < H; y++) {
            for(int x = 0; x < W; x++) {
                int i = y * W + x;
                List<int> next = new List<int>();
                if(x == 0) {
                    if(x != W - 1) {
                        if(S[y][x + 1] == '.') next.Add(i + 1);
                    }
                }else if(x == W - 1) {
                    if(S[y][x - 1] == '.') next.Add(i - 1);
                } else {
                    if(S[y][x + 1] == '.') next.Add(i + 1);
                    if(S[y][x - 1] == '.') next.Add(i - 1);
                }
                if(y == 0) {
                    if(y != H - 1) {
                        if(S[y + 1][x] == '.') next.Add(i + W);
                    }
                }else if(y == H - 1) {
                    if(S[y - 1][x] == '.') next.Add(i - W);
                } else {
                    if(S[y + 1][x] == '.') next.Add(i + W);
                    if(S[y - 1][x] == '.') next.Add(i - W);
                }
                graph[i] = next.ToArray();
            }
        }
        int max = 0;
        for(int y = 0; y < H; y++) {
            for(int x = 0; x < W; x++) {
                if(S[y][x] == '.') {

                    int root = y * W + x;
                    int[] depth = new int[H * W];
                    depth[root] = 0;
                    //幅優先探索開始
                    bool[] seen = new bool[n];
                    Queue<int> queue = new Queue<int>();
                    queue.Enqueue(root);
                    while(0 < queue.Count) {
                        int dequeued = queue.Dequeue();
                        //各頂点に対する処理
                        //探索対象でないならばその子要素をキューに追加する
                        foreach(var node in graph[dequeued]) {
                            if(!seen[node]) {
                                seen[node] = true;
                                queue.Enqueue(node);
                                depth[node] = depth[dequeued] + 1;
                            }
                        }
                    }
                    //探索終了
                    int _max = 0;
                    for(int i = 0; i < H * W; i++) {
                        if(_max < depth[i]) {
                            _max = depth[i];
                        }
                    }
                    if(max < _max) {
                        max = _max;
                    }
                }
            }
        }
        Console.WriteLine(max);
    }
    static T ChangeT<T>(string s) => (T)Convert.ChangeType(s, typeof(T));
    static void Input<A>(out A a) {
        var words = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries);
        a = ChangeT<A>(words[0]);
    }
    static void Input<A, B>(out A a, out B b) {
        var words = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries);
        a = ChangeT<A>(words[0]);
        b = ChangeT<B>(words[1]);
    }
    static void Input<A, B, C>(out A a, out B b, out C c) {
        var words = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries);
        a = ChangeT<A>(words[0]);
        b = ChangeT<B>(words[1]);
        c = ChangeT<C>(words[2]);
    }
    static void Input<A, B, C, D>(out A a, out B b, out C c, out D d) {
        var words = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries);
        a = ChangeT<A>(words[0]);
        b = ChangeT<B>(words[1]);
        c = ChangeT<C>(words[2]);
        d = ChangeT<D>(words[3]);
    }
    static void Input<A, B, C, D, E>(out A a, out B b, out C c, out D d, out E e) {
        var words = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries);
        a = ChangeT<A>(words[0]);
        b = ChangeT<B>(words[1]);
        c = ChangeT<C>(words[2]);
        d = ChangeT<D>(words[3]);
        e = ChangeT<E>(words[4]);
    }
    static void Input<A, B, C, D, E, F>(out A a, out B b, out C c, out D d, out E e, out F f) {
        var words = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries);
        a = ChangeT<A>(words[0]);
        b = ChangeT<B>(words[1]);
        c = ChangeT<C>(words[2]);
        d = ChangeT<D>(words[3]);
        e = ChangeT<E>(words[4]);
        f = ChangeT<F>(words[5]);
    }
    static void InputArray<T>(out T[] array) {
        array = Console.ReadLine().Split(new[] { ' ' }, options: StringSplitOptions.RemoveEmptyEntries).Select(s => ChangeT<T>(s)).ToArray();
    }
}