### BUGGY CODE ###

from collections import deque 
from copy import deepcopy
# 初期入力
import sys
input = sys.stdin.readline
H,W = (int(i) for i in input().split())
map_initial =[["#"]*(W+2) for i in range(H+2)] #周囲を壁にするため＋２
for h in range(1,H+1):
    map_initial[h] =["#"] +list(input().strip()) +["#"]

def BSF(x,y):
    dist =0
    map =deepcopy(map_initial)
    if map[x][y] =="#":
        return dist 
    dq =deque()
    dq.append((x,y))
    dq_sarch =deque()
    while len(dq) >0:
        h,w =dq.popleft()
        map[h][w] ="#" #通り済を壁にする
        if map[h+1][w]==".":
            dq_sarch.append((h+1,w))
        if map[h-1][w]==".":
            dq_sarch.append((h-1,w))
        if map[h][w+1]==".":
            dq_sarch.append((h,w+1))
        if map[h][w-1]==".":
            dq_sarch.append((h,w-1))
        if len(dq)==0:
            dq =deepcopy(dq_sarch)
            dq_sarch.clear()
            dist +=1
        #print(h,w,dist,end="\t")
    return dist-1

#スタート位置を全探索し、最長距離を探す
dist_all =[]
for i in range(1,H+1):
    for j in range(1,W+1):
        dist_all.append(BSF(i,j) )
print(max(dist_all))

### FIXED CODE ###

"""参考
@drken
2020年02月02日に更新
BFS (幅優先探索) 超入門！ 〜 キューを鮮やかに使いこなす 〜
https://qiita.com/drken/items/996d80bcae64649a6580
"""
import numpy as np 
from copy import deepcopy,copy
from copy import copy
def Breadth_first_search(start,already_initial):#幅優先探索
    already=set()
    already =copy(already_initial)
    already.add(start)    
    now = start
    now_generation =copy(queue_dic[start]) #次に行けるところ
    next_preparation =set() #その世代の間、nextに行くために溜めておく
    count_generation=0
    while len(already) < (H+2)*(W+2):
        for now in now_generation:
            if now in already:          #今いるのが壁
                continue            #壁はalready入力すみ
            elif now_generation <= already:   #壁・alreadyに囲まれている
                already.add(now)
                continue
            else:
                #print(now,end=" ")
                already.add(now)
                next_preparation    |= queue_dic[now] 

        now_generation.clear()
        now_generation           |= next_preparation
        next_preparation.clear()
        count_generation +=1
    return count_generation 

# 初期入力

H,W = (int(x) for x in input().split())

map_np =np.full((H+2,W+2),"#",dtype=str) #グリッド初期化、壁で囲むので＋２
for i in range(1,H+1):
    S = input()
    S.strip()
    for j in range(1,W+1):
        map_np[i,j]=S[j-1]

# 構造ーつながり
queue_dic={(h,w):set()for h in range(1,H+1) for w in range(1,W+1)}
already_initial=set() #探索すみ座標の初期値
for h in range(H+2):
    for w in range(W+2):
        if map_np[h,w] ==".":
            if map_np[h+1,w] =="." :
                queue_dic[h,w].add((h+1  ,w  ))
            if map_np[h-1,w] =="." :
                queue_dic[h,w].add((h-1  ,w  ))
            if map_np[h,w+1] =="." :
                queue_dic[h,w].add((h    ,w+1))
            if map_np[h,w-1] =="." :
                queue_dic[h,w].add((h    ,w-1))
        else:
            already_initial.add((h,w)) #壁は行けない＝既に行った
grid_all =queue_dic.keys() #マップの全て
can_pass =grid_all - already_initial  #通過できるところ

#幅優先探索
distance_list =[] #各スタート場所ごとの距離
for start1 in can_pass:
    already1 =set()
    already1=copy(already_initial )
    #print(already_initial)  
    distance_from_start =Breadth_first_search(start1,already_initial)
    distance_list.append(distance_from_start)

print(max(distance_list))