using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
namespace Atcoder_0404
{
    class Program
    {
        class Pos
        {
            public int x;
            public int y;
            public int distance=0;
            public Pos(int x,int y,int distance)
            {
                this.x = x;
                this.y = y;
                this.distance = distance;
            }

            public bool EqualPos(Pos target)
            {
                return x == target.x && y == target.y;
            }
            public static bool CheckInField(int h,int w,Pos pos)
            {
                return (0 <= pos.x && pos.x < w
                    && 0 <= pos.y && pos.y < h);
            }
        }

        

        static void Main(string[] args)
        {
            var HW = GetInput_int();
            string[] _maze = new string[HW[0]];
            for(int i = 0; i < HW[0]; i++)
            {
                _maze[i] = GetInput_string()[0];
            }
            int max = 0;
            for(int h = 0; h < HW[0]; h++)
            {
                for(int w = 0; w < HW[1]; w++)
                {
                    if (_maze[h][w] == '#') continue;
                    var start = new Pos(w, h,0);

                    //DFSを使ってゴールを決める
                    Queue<Pos> _queue = new Queue<Pos>();
                    Dictionary<Pos, int> _checkedSets = new Dictionary<Pos, int>();
                    _queue.Enqueue(start);
                    while (_queue.Count>0)
                    {
                        //foreach (var data in _queue)
                        //{
                        //    Console.Write(string.Format("({0},{1}),", data.x, data.y));
                        //}
                        //Console.Write("\n");
                        var target = _queue.Dequeue();
                        _checkedSets.Add(target,target.distance);

                        //時計回りに周囲を確認
                        Queue<Pos> tempstack = new Queue<Pos>();
                        tempstack.Enqueue(new Pos(target.x, target.y+1, target.distance + 1));
                        tempstack.Enqueue(new Pos(target.x+1, target.y, target.distance + 1));
                        tempstack.Enqueue(new Pos(target.x, target.y-1, target.distance + 1));
                        tempstack.Enqueue(new Pos(target.x-1, target.y, target.distance + 1));
                        while (tempstack.Count > 0)
                        {
                            var temp = tempstack.Dequeue();
                            if (!(0 <= temp.x && temp.x < HW[1]
                               && 0 <= temp.y && temp.y < HW[0])) continue;
                            if (_maze[temp.y][temp.x] == '#') continue;
                            if (_checkedSets.Keys.Where(x => x.EqualPos(temp)).FirstOrDefault() != null) continue;
                            if (_queue.Where(x => x.EqualPos(temp)).FirstOrDefault() != null) continue;
                            _queue.Enqueue(temp);
                        }
                    }
                    int tempMax = _checkedSets.Values.Max();
                    //Console.WriteLine("max:" + tempMax);
                    if (tempMax > max) max = tempMax;
                }
            }
            Console.WriteLine(max);
        }
        #region input
        

        static string[] GetInput_string()
        {
            var input = Console.ReadLine().Split(' ');
            return input;
        }

        static int[] GetInput_int()
        {
            var input = GetInput_string();
            var result = new int[input.Length];
            for (int i = 0; i < result.Length; i++)
            {
                result[i] = int.Parse(input[i]);
            }
            return result;
        }
        static long[] GetInput_long()
        {
            var input = GetInput_string();
            var result = new long[input.Length];
            for (int i = 0; i < result.Length; i++)
            {
                result[i] = long.Parse(input[i]);
            }
            return result;
        }
        #endregion
        #region sonota
        static void Swap_array<T>(T[] target, int a, int b)
        {
            T temp = target[a];
            target[a] = target[b];
            target[b] = temp;
        }
        static void Swap_list<T>(List<T> target, int a, int b)
        {
            T temp = target[a];
            target[a] = target[b];
            target[b] = temp;
        }
        /// <summary>
        /// 最大公約数
        /// </summary>
        public static int GCD(int a, int b)
        {

            /* 自然数 a > b を確認・入替 */
            if (a < b)
            {
                int tmp = a;
                a = b;
                b = tmp;
            }

            /* ユークリッドの互除法 */
            int r = a % b;
            while (r != 0)
            {
                a = b;
                b = r;
                r = a % b;
            }
            return b;
        }

        /// <summary>
        /// 最小公倍数
        /// </summary>
        public static int LCM(int a, int b)
        {
            int x = a * b;
            b = GCD(a, b);
            return x / b;

        }
        #endregion

        static long mod = 1000000000 + 7;
    }
}