### BUGGY CODE ###

# coding: utf-8
import sys
#from operator import itemgetter
sysread = sys.stdin.buffer.readline
read = sys.stdin.buffer.read
#from heapq import heappop, heappush
#from collections import defaultdict
sys.setrecursionlimit(10**7)
#import math
#from itertools import product, accumulate, combinations, product
#import bisect
#import numpy as np
#from copy import deepcopy
#from collections import deque
#from decimal import Decimal
#from numba import jit

INF = 1 << 50

def dfs(MAZE, goal, current, seen, H, W, min_count = INF):
    y, x, count = current
    #print(x,y,count)
    if count > min_count:
        return min_count
    if (y,x) == goal:
        min_count = min(min_count, count)
    else:
        for i,j in ((0,1), (0,-1), (1,0), (-1, 0)):
            next_y, next_x = y+i, x+j
            if 1 <= next_x <= W and 1 <= next_y <= H and not seen[next_y][next_x] and MAZE[next_y][next_x] == '.':
                seen[next_y][next_x] = 1
                min_count = min(dfs(MAZE, goal, (next_y, next_x, count+1), seen, H, W, min_count), min_count)
                seen[next_y][next_x] = 0
    return min_count

def run():
    H, W = map(int, input().split())
    MAZE = ['#' * (W+2)]
    for _ in range(H):
        s = input()
        s = '#' + s + '#'
        MAZE.append(s)
    MAZE.append('#' * (W+2))

    seen = [[0] * (W+2) for i in range(H+1)]

    max_count = 0
    for s_i in range(1, H+1):
        for s_j in range(1, W+1):
            for g_i in range(1, H + 1):
                for g_j in range(1, W + 1):
                    if s_i == g_i and s_j == g_j:continue
                    if MAZE[s_i][s_j] == '#' or MAZE[g_i][g_j] == '#':continue
                    seen[s_i][s_j] = 1
                    min_count = dfs(MAZE, (g_i, g_j), (s_i, s_j, 0), seen, H, W)
                    #print((s_i, s_j), '->', (g_i, g_j))
                    #print(min_count)
                    if min_count == INF:
                        continue
                    max_count = max(max_count, min_count)
                    seen[s_i][s_j] = 0

    print(max_count)

if __name__ == "__main__":
    run()

### FIXED CODE ###

# coding: utf-8
import sys
#from operator import itemgetter
sysread = sys.stdin.buffer.readline
read = sys.stdin.buffer.read
#from heapq import heappop, heappush
#from collections import defaultdict
sys.setrecursionlimit(10**7)
#import math
#from itertools import product, accumulate, combinations, product
#import bisect
#import numpy as np
#from copy import deepcopy
from collections import deque
#from decimal import Decimal
#from numba import jit

INF = 1 << 50

def bfs(MAZE, start, seen):
    queue = deque([start + (0 ,)])
    max_count = 0
    while queue:
        y,x,count = queue.popleft()
        max_count = max(max_count, count)
        if (y,x) in seen:
            continue
        seen.add((y, x))
        for i,j in ((0,1), (0,-1), (1,0), (-1, 0)):
            next_y, next_x = y + i, x + j
            #print(next_y, next_x)
            if not (next_y, next_x) in seen and MAZE[next_y][next_x] == '.':
                queue.append((next_y, next_x, count+1))
                #seen.add((next_y, next_x))
    return max_count


def run():
    H, W = map(int, input().split())
    MAZE = ['#' * (W+2)]
    for _ in range(H):
        s = input()
        s = '#' + s + '#'
        MAZE.append(s)
    MAZE.append('#' * (W+2))

    #for m in MAZE:
    #    print(m)

    ways = []
    for i in range(1, H+1):
        for j in range(1, W+1):
            if MAZE[i][j] == '.':
                ways.append((i, j))

    max_count = 0
    for s_i, s_j in ways:
        seen = set((s_i, s_j))
        min_count = bfs(MAZE, (s_i, s_j), seen)
        #print((s_i, s_j), '->', (g_i, g_j))
        #print(min_count)
        max_count = max(max_count, min_count)

    print(max_count)

if __name__ == "__main__":
    run()